workstation
Journaux liées à cette note :
Journal du lundi 09 décembre 2024 à 15:50
J'utilise la fonctionnalité Docker volume mounts dans tous mes projets depuis septembre 2015.
Généralement, sous la forme suivante :
services:
postgres:
image: postgres:17
...
volumes:
- ./volumes/postgres/:/var/lib/postgresql/data/
D'après mes recherches, la fonctionnalité volumes mounts a été introduite dans la version 0.5.0 en juillet 2013.
À cette époque, je crois me souvenir que Docker permettait aussi de créer des volumes anonymes.
Je n'ai jamais apprécié les volumes anonymes, car lorsqu'un conteneur était supprimé, il devenait compliqué de retrouver le volume associé.
À cette époque, Docker était nouveau et j'avais très peur de perdre des données, par exemple, les volumes d'une instance PostgreSQL.
J'ai donc décidé qu'il était préférable de renoncer aux volumes anonymes et d'opter systématiquement pour des volume mounts.
Ensuite, peut-être en janvier 2016, Docker a introduit les named volumes, qui permettent de créer des volumes avec des noms précis, par exemple :
services:
postgres:
image: postgres:17
...
volumes:
- postgres:/var/lib/postgresql/data/
volumes:
postgres:
name: postgres
$ docker volume ls
DRIVER VOLUME NAME
local postgres
Ce volume est physiquement stocké dans le dossier /var/lib/docker/volumes/postgres/_data
.
Depuis, j'ai toujours préféré les volumes mounts aux named volumes pour les raisons pratiques suivantes :
- Travaillant souvent sur plusieurs projets, j'utilise les volume mounts pour éviter les collisions. Lorsque j'ai essayé les named volumes, une question s'est posée : quel nom attribuer aux volumes PostgreSQL ? «
postgres
» ? Mais alors, quel nom donner au volume PostgreSQL dans le projet B ? Avec les volume mounts, ce problème ne se pose pas. - J'apprécie de savoir qu'en supprimant un projet avec
rm -rf ~/git/github.com/stephan-klein/foobar/
, cette commande effacera non seulement l'intégralité du projet, mais également ses volumes Docker. - Avec les mounted volume, je peux facilement consulter le contenu des volumes. Je n'ai pas besoin d'utiliser
docker volume inspect
pour trouver le chemin du volume.
La stratégie que j'ai choisie basée sur volumes mounts a quelques inconvénients :
- Le owner du dossier
volumes/
, situé dans le répertoire du projet, estroot
. Cela entraîne fréquemment des problèmes de permissions, par exemple lors de l'exécution des scripts de linter dans le dossier du projet. Pour supprimer le projet, je dois donc utilisersudo
. Je précise que ce problème n'existe pas sous MacOS. Je pense que ce problème pourrait être contourné sous Linux en utilisant podman. - La commande
docker compose down -v
ne détruit pas les volumes.
Je suis pleinement conscient que ma méthode basée sur les volume mounts est minoritaire. En revanche, j'observe qu'une grande majorité des développeurs privilégie l'utilisation des named volumes.
Par exemple, cet été, un collègue a repris l'un de mes projets, et l'une des premières choses qu'il a faites a été de migrer ma configuration de volume mounts vers des named volumes pour résoudre un problème de permissions lié à Prettier, eslint ou Jest. En effet, la fonctionnalité ignore de ces outils ne fonctionne pas si NodeJS n'a pas les droits d'accès à un dossier du projet 😔.
Aujourd'hui, je me suis lancé dans la recherche d'une solution me permettant d'utiliser des named volumes tout en évitant les problèmes de collision entre projets.
Je pense que j'ai trouvé une solution satisfaisante 🙂.
Je l'ai décrite et testée dans le repository docker-named-volume-playground
.
Ce repository d'exemple contient 2 projets distincts, nommés project_a
et project_b
.
J'ai instancié deux fois chacun de ces projets. Voici la liste des dossiers :
$ tree
.
├── project_a_instance_1
│ ├── docker-compose.yml
│ └── .envrc
├── project_a_instance_2
│ ├── docker-compose.yml
│ └── .envrc
├── project_b_instance_1
│ ├── docker-compose.yml
│ └── .envrc
├── project_b_instance_2
│ ├── docker-compose.yml
│ └── .envrc
└── README.md
Ce repository illustre l'organisation de plusieurs instances de différents projets sur la workstation du développeur.
Il ne doit pas être utilisé tel quel comme base pour un projet.
Par exemple, le "vrai" repository du projet projet_a
se limiterait aux fichiers suivants : docker-compose.yml
et .envrc
.
Voici le contenu d'un de ces fichiers .envrc
:
export PROJECT_NAME="project_a"
export INSTANCE_ID=$(pwd | shasum -a 1 | awk '{print $1}' | cut -c 1-12) # Used to define docker volume path
export COMPOSE_PROJECT_NAME=${PROJECT_NAME}_${INSTANCE_ID}
L'astuce que j'utilise est au niveau de INSTANCE_ID
. Cet identifiant est généré de telle manière qu'il soit unique pour chaque instance de projet installée sur la workstation du développeur.
J'ai choisi de générer cet identifiant à partir du chemin complet vers le dossier de l'instance, je le passe dans la commande shasum
et je garde les 12 premiers caractères.
J'utilise ensuite la valeur de COMPOSE_PROJECT_NAME
dans le docker-compose.yml
pour nommer le named volume :
services:
postgres:
image: postgres:17
environment:
POSTGRES_USER: postgres
POSTGRES_DB: postgres
POSTGRES_PASSWORD: password
ports:
- 5432
volumes:
- postgres:/var/lib/postgresql/data/
healthcheck:
test: ["CMD", "sh", "-c", "pg_isready -U $$POSTGRES_USER -h $$(hostname -i)"]
interval: 10s
start_period: 30s
volumes:
postgres:
name: ${COMPOSE_PROJECT_NAME}_postgres
Exemples de valeurs générées pour l'instance installée dans /home/stephane/git/github.com/stephane-klein/docker-named-volume-playground/project_a_instance_1
:
INSTANCE_ID=d4cfab7403e2
COMPOSE_PROJECT_NAME=project_a_d4cfab7403e2
- Nom du container postgresql :
project_a_d4cfab7403e2-postgres-1
- Nom du volume postgresql :
project_a_a04e7305aa09_postgres
Conclusion
Cette méthode me permet de suivre une pratique plus mainstream — utiliser les named volumes Docker — tout en évitant la collision des noms de volumes.
Je suis conscient que ce billet est un peu long pour expliquer quelque chose de simple, mais je tenais à partager l'historique de ma démarche.
Je pense que je vais dorénavant utiliser cette méthode pour tous mes nouveaux projets.
20224-12-10 11h27 : Je tiens à préciser qu'avec la configuration suivante :
services:
postgres:
image: postgres:17
...
volumes:
- postgres:/var/lib/postgresql/data/
volumes:
postgres:
Quand le nom du volume postgres
n'est pas défini, docker-compose le nomme sous la forme ${COMPOSE_PROJECT_NAME}_postgres
. Si le projet est stocké dans le dossier foobar
, alors le volume sera nommé foobar_postgres
.
$ docker volume ls
DRIVER VOLUME NAME
local foobar_postgres
À la recherche d'un environnement de développement sans le savoir : mes années 1999-2008
Je pense que je m'intéresse au sujet des "development environment" ou development kit depuis 1999.
À l'époque, même si je ne connaissais pas encore ces concepts, j'en ressentais déjà le besoin.
Par exemple, lorsque j'ai voulu contribuer au projet KDE, je cherchais des solutions et des bonnes pratiques pour organiser ma station de travail de manière à pouvoir compiler et installer KDE sans risquer de casser mon environnement de bureau.
Malheureusement, je n'y suis pas parvenu 😔.
Avec le recul, je réalise que ces pratiques étaient souvent transmises de manière informelle, principalement par des échanges oraux ou via des messages sur IRC. En somme, il s'agissait d'un véritable "bouche-à-oreille" numérique.
Je pense que la majorité des développeurs devaient faire face aux mêmes difficultés. Je pense aussi qu’ils devaient prendre beaucoup de temps à trouver leur propre méthode, année après année.
J’en ai eu une fois la confirmation. En 2002, je suis allé au FOSDEM, pour rencontrer entre autres David Faure, un core développeur français de KDE.
J’ai échangé avec lui et il m’a montré de nombreuses astuces qu’il utilisait… je n’ai pas tout compris, je n’ai pas réussi à les retenir…
J’en ai conclu que les développeurs avaient sans doute chacun leurs propres méthodes de développement non documentées.
Question qui me vient alors à l’esprit : alors qu'ils contribuent à des logiciels libres et partagent leur code source, pourquoi les développeurs ne partagent-ils pas aussi leurs environnements de développement ?
Journal du vendredi 18 octobre 2024 à 19:15
Nouvelle #iteration de Projet 14.
Pour traiter ce problème, je souhaite essayer de remplacer Vagrant Host Manager par vagrant-dns.
-- from
Résultat : j'ai migré de Vagrant Host Manager vers vagrant-dns avec succès 🙂.
Voici le commit : lien vers le commit.
Voici quelques explications de la configuration Vagrantfile.
Les lignes suivantes permettent d'utiliser la seconde IP des machines virtuelles pour identifier les identifier (renseignés par le serveur DNS).
config.dns.ip = -> (vm, opts) do
ip = nil
vm.communicate.execute("hostname -I | cut -d ' ' -f 2") do |type, data|
ip = data.strip if type == :stdout
end
ip
end
La commande hostname
retourne les deux IP de la machine virtuelle :
vagrant@server1:~$ hostname -I
10.0.2.15 192.168.56.22
La commande hostname -I | cut -d ' ' -f 2
capture la seconde IP, ici 192.168.56.22
.
La configuration DNS qui retourne cette IP est consultable via :
$ vagrant dns -l
/server1.vagrant.test/ => 192.168.56.22
/server2.vagrant.test/ => 192.168.56.23
/grafana.vagrant.test/ => 192.168.56.23
/loki.vagrant.test/ => 192.168.56.23
vb.customize ["modifyvm", :id, "--natdnshostresolver1", "on"]
Cette ligne configure la machine virtuelle pour qu'elle utilise le serveur DNS de vagrant-dns.
Cela permet de résoudre les noms des autres machines virtuelles. Exemple :
vagrant@server1:~$ resolvectl query server2.vagrant.test
server2.vagrant.test: 192.168.56.23 -- link: eth0
-- Information acquired via protocol DNS in 12.3ms.
-- Data is authenticated: no; Data was acquired via local or encrypted transport: no
-- Data from: network
En mettant en place vagrant-dns sur ma workstation qui tourne sous Fedora, j'ai rencontré la difficulté suivante.
J'avais la configuration suivante installée :
$ cat /etc/systemd/resolved.conf.d/csd.conf
[Resolve]
DNS=10.57.40.1
Domains=~csd
Elle me permet de résoudre les hostnames des machines qui appartiennent à un réseau privé exposé via OpenVPN (voir cette note).
Voici ma configuration complète de systemd-resolved
:
$ systemd-analyze cat-config systemd/resolved.conf
# /etc/systemd/resolved.conf
...
[Resolve]
...
# /etc/systemd/resolved.conf.d/1-vagrant-dns.conf
# This file is generated by vagrant-dns
[Resolve]
DNS=127.0.0.1:5300
Domains=~test
# /etc/systemd/resolved.conf.d/csd.conf
[Resolve]
DNS=10.57.40.1
Domains=~csd
Quand je lançais resolvectl query server2.vagrant.test
pour la première fois après redémarrage de sudo systemctl restart systemd-resolved
, tout fonctionnait correctement :
$ resolvectl query server2.vagrant.test
server2.vagrant.test: 192.168.56.23
-- Information acquired via protocol DNS in 7.5073s.
-- Data is authenticated: no; Data was acquired via local or encrypted transport: no
-- Data from: network
Mais, la seconde fois, j'avais l'erreur suivante :
$ resolvectl query server2.vagrant.test
server2.vagrant.test: Name 'server2.vagrant.test' not found
Ce problème disparait si je supprime /etc/systemd/resolved.conf.d/csd.conf
.
Je n'ai pas compris pourquoi. D'après la section "Protocols and routing" de systemd-resolved, le serveur 10.57.40.1
est utilisé seulement pour les hostnames qui se terminent par .csd
.
J'ai activé les logs de systemd-resolved au niveau debug
avec
$ sudo resolvectl log-level debug
$ journalctl -u systemd-resolved -f
Voici le contenu des logs lors de la première exécution de resolvectl query server2.vagrant.test
: https://gist.github.com/stephane-klein/506a9fc7d740dc4892e88bfc590bee98.
Voici le contenu des logs lors de la seconde exécution de resolvectl query server2.vagrant.test
: https://gist.github.com/stephane-klein/956befc280ef9738bfe48cdf7f5ef930.
J'ai l'impression que la ligne 13 indique que le cache de systemd-resolved a été utilisé et qu'il n'a pas trouvé de réponse pour server2.vagrant.test
. Pourquoi ? Je ne sais pas.
Ligne 13 : NXDOMAIN cache hit for server2.vagrant.test IN A
Ensuite, je supprime /etc/systemd/resolved.conf.d/csd.conf
:
$ sudo rm /etc/systemd/resolved.conf.d/csd.conf
Je relance systemd-resolved et voici le contenu des logs lors de la seconde exécution de resolvectl query server2.vagrant.test
: https://gist.github.com/stephane-klein/9f87050524048ecf9766f9c97b789123#file-systemd-resolved-log-L11
Je constate que cette fois, la ligne 11 contient : Cache miss for server2.vagrant.test IN A
.
Pourquoi avec .csd
le cache retourne NXDOMAIN
et sans .csd
, le cache retourne Cache miss
et systemd-resolved continue son algorithme de résosultion du hostname ?
Je soupçonne systemd-resolved de stocker en cache la résolution de server2.vagrant.test
par le serveur DNS 10.57.40.1
. Si c'est le cas, je me demande pourquoi il fait cela alors qu'il est configuré pour les hostnames qui se terminent par .csd
🤔.
Autre problème rencontré, la latence de réponse :
$ resolvectl query server2.vagrant.test
server2.vagrant.test: 192.168.56.23
-- Information acquired via protocol DNS in 7.5073s.
-- Data is authenticated: no; Data was acquired via local or encrypted transport: no
-- Data from: network
La réponse est retournée en 7 secondes, ce qui ne me semble pas normal.
J'ai découvert que je n'ai plus aucune latence si je passe le paramètre DNSStubListener
à no
:
$ sudo cat <<EOF > /etc/systemd/resolved.conf.d/0-vagrant-dns.conf
[Resolve]
DNSStubListener=no
EOF
$ sudo systemctl restart systemd-resolved
$ resolvectl query server2.vagrant.test
server2.vagrant.test: 192.168.56.23
-- Information acquired via protocol DNS in 2.9ms.
-- Data is authenticated: no; Data was acquired via local or encrypted transport: no
-- Data from: network
Le temps de réponse passe de 7.5s
à 2.9ms
. Je n'ai pas compris la signification de ma modification.
Je récapitule, pour faire fonctionner correctement vagrant-dns sur ma workstation Fedora j'ai dû :
- supprimer
/etc/systemd/resolved.conf.d/csd.conf
- et paramétrer
DNSStubListener
àno
Migration de vagrant-hostmanager vers vagrant-dns
Dans le cadre du Projet 14 - Script de base d'installation d'un serveur Ubuntu LTS, j'utilise Vagrant avec le plugin Vagrant Host Manager pour gérer les hostnames des machines virtuelles.
Vagrant Host Manager met correctement à jour les fichiers /etc/hosts
sur ma machine hôte, c'est-à-dire, ma workstation, et dans les machines virtuels.
Cependant, ces noms d'hôtes ne sont pas accessibles à l'intérieur des containers Docker.
Par exemple, ici, http://grafana.example.com:3100/
n'est pas accessible à l'intérieur du container Promtail.
Pour traiter ce problème, je souhaite essayer de remplacer Vagrant Host Manager par vagrant-dns.
vagrant-dns semble exposer un serveur DNS qui sera accessible et utilisé par les containers Docker.
D'autre part, vagrant-dns (page contributors) semble un peu plus actif que Vagrant Host Manager (page contributors).
Journal du jeudi 12 septembre 2024 à 19:14
#JaiDécouvert cet article pnpm "Working with Docker".
J'y ai découvert corepack.
Pour le moment, je ne comprends pas l'avantage d'utiliser :
FROM node:20-slim AS base
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
RUN corepack enable
plutôt que :
FROM node:20-slim AS base
RUN npm install -g pnpm@9.10
🤔
Dans ce Dockerfile j'ai tout de même utilisé cette technique pour tester.
J'ai utilisé le système de cache store de pnpm :
RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --prod --frozen-lockfile
Je me suis posé la question de partage le cache de ma workstation :
$ pnpm store path
/home/stephane/.local/share/pnpm/store/v3
Mais je ne pense pas que cela soit une bonne idée dans le cas où cette image est buildé par une CI.
Journal du mercredi 14 août 2024 à 14:23
Je dois passer par un VPN pour accéder à un projet professionnel.
Ce VPN est propulsé par OpenVPN.
Ma workstation tourne sous Fedora, sous GNOME.
J'ai réussi à configurer facilement l'accès OpenVPN via NetworkManager-openvpn via l'import de fichier .ovpn
.
Cependant, cette méthode de configuration m'a posé un problème : le routage par défaut était dirigé vers le VPN. Conséquence, l'intégration de mon accès à Internet passait par le réseau VPN qui était bien plus lent que mon accès Internet.
Ceci était très frustrant.
De plus, cette configuration ajoutait une charge réseau supplémentaire inutile au VPN.
J'ai essayé d'utiliser l'option "N'utiliser cette connexion que pour les ressources sur ce réseau" mais cela n'a pas fonctionné.
Peut-être un problème DNS 🤔.
J'ai donc choisi une autre stratégie. J'ai configuré cela sans GUI.
Voici le skeleton de mon dossier de connexion au VPN : https://github.com/stephane-klein/openvpn-client-skeleton (celui-ci ne contient aucun secret).
Ce projet me permet :
- D'utiliser le serveur DNS présent dans le réseau privé seulement pour un certain type de sous domaine.
- Le VPN est utilisé uniquement pour les serveurs qui se trouvent à l'intérieur du réseau privé. Par exemple, je ne passe pas par ce VPN pour accéder à Internet.
- La totalité de cette configuration est basé sur des fichiers et est scripté (pratique GitOps)
- OpenVPN client est managé par SystemD
Voir aussi 2024-08-14_1511.
Upgrade de ma workstation de Fedora 39 vers 40
Fedora 40 version stable est sortie le 23 avril 2024 et presque un mois plus tard, j'ai upgrade mon Thinkpad T14s de la version 39 vers la version 40.
Que ce soit par le passé avec MacOS et maintenant avec Fedora, pour éviter d'être impacté par des bugs, ou des régressions, j'ai pris l'habitude d'attendre quelques semaines avant d'effectuer un upgrade d'OS majeur de ma workstation.
J'ai suivi la méthode officielle de mise à jour :
# dnf install dnf-plugin-system-upgrade
# dnf upgrade --refresh
# dnf clean all
# dnf system-upgrade download --releasever=40
# dnf system-upgrade reboot
et cela c'est déroulé avec succès.
Après 1h d'utilisation, je n'ai observé aucune régression.