Table des matières

2023/06/19 (atelier) : Ansible

Présences :

Météo

Moment informel durant lequel on exprime en peu de mots comment on se sent et si on a une attente forte pour la réunion. Ce n'est pas un moment de discussion mais d'expression individuelle et ce n'est pas obligatoire 🙂

Attente(s) forte(s)

Si l'une ou l'autre personne exprime une attente forte, merci de vous en occuper en priorité ou de la noter dans le hub ou dans un point approprié.

Fin: 17h30

Anciens TODOs

Configuration

GeoCoder

Sur mobilizon.be, on utilise Nominatim comme geocoder, à voir dans la doc s'il faut installer des trucs: https://docs.joinmobilizon.org/administration/configure/geocoders/

D'autres Géocoders sont possibles mais c'est vite assez cher.

Pour le moment on garde Nominatim, et on verra plus tard s'il faut changer. Il faudra regarder combien de requêtes par mois sont faites vers Nominatim.

SMTP

Il reste encore à configurer Mobilizon pour envoyer des mails.

On peut regarder comment c'est fait sur mobilizon.be

Sur le serveur, cela se trouve dans le dossier live/config/runtime.exs. Il y a une partie pour le SMTP:

config :mobilizon, Mobilizon.Web.Email.Mailer,

On réutilise la config SMTP, mais on la changera sans doute plus tard quand on aura les accès au nom de domaine (création d'une boîte mail chez Neutrinet ?)

Postgis

Il faut qu'on installe le plugin postgresql-13-postgis sur tous les hosts postgresql, car sinon la base de données de Mobilizon se casse la figure lorsqu'un autre serveur devient le serveur primaire.

On doit faire une boucle dans le rôle ansible, car la directive delegated_to ne permet pas d'indiquer plusieurs hosts.

On peut utiliser la variable postgresql_patroni_hosts qui contient la liste des serveurs du cluster patroni.

On utilise la directive loop pour parcourir la liste des serveurs postgresql définis dans la variable postgresql_patroni_hosts, et pour chaque serveur dans la liste on délègue la tâche vers celui-ci:

snippet.yaml
delegate_to: "{{ postgresql_host }}"
loop: "{{ postgresql_patroni_hosts }}"
loop_control:
  loop_var: postgresql_host

Le problème, c'est qu'il y a deux cas différents. Lorsque le playbook est exécuté sur une machine qui fait partie d'un cluster patroni, la liste définie dans la variable postgresql_patroni_hosts n'est pas vide et la tâche se déroule bien. Cependant, lorsque le rôle est lancé localement avec molécule, la tâche n'est pas effectuée car la liste des hosts patroni est vide.

Une première possibilité est d'écrire deux fois la tâche. Une première fois pour le cas où le playbook est lancé sur une machine qui fait partie d'un cluster (on ne lance alors la tâche que lorsque la variable contient plus d'éléments que 0 en utilisant l'opérateur when).

snippet.yaml
- name: Installation de la dépendance postgresql pour postgis
  ansible.builtin.package:
    name:
      - postgresql-{{ postgresql_major_version }}-postgis
    state: present
  delegate_to: "{{ postgresql_host }}"
  loop: "{{ postgresql_patroni_hosts }}"
  loop_control:
    loop_var: postgresql_host
  when: postgresql_patroni_hosts | length > 0

Puis une seconde fois pour le cas où la liste est vide et où la tâche n'a pas été appliquée, en spécifiant de ne faire cette opération que lorsque la liste est vide :

snippet.yaml
- name: Installation de la dépendance postgresql pour postgis
  ansible.builtin.package:
    name:
      - postgresql-{{ postgresql_major_version }}-postgis
    state: present
  delegate_to: "{{ inventory_hostname }}"
  when: postgresql_patroni_hosts | length == 0

(On note l'opérateur when qui change, dans un cas avec length > 0 et une boucle, et dans l'autre avec length == 0 sans boucle)

L'avantage de cette méthode : cela rend le code lisible. L'inconvénient, c'est que ce n'est pas très optimisé et c'est très verbeux.

snippet.yaml
- name: Installation de la dépendance postgresql pour postgis
  ansible.builtin.package:
    name:
      - postgresql-{{ postgresql_major_version }}-postgis
    state: present
  delegate_to: "{{ postgresql_host }}"
  loop: "{{ postgresql_patroni_hosts if (postgresql_patroni_hosts | length > 0) else [inventory_hostname] }}"
  loop_control:
    loop_var: postgresql_host

Dans la directive loop, on dit : « Prend la liste définie dans la variable postgresql_patroni_hosts, uniquement si la liste contient des éléments. Sinon, prend la liste qui contient l'élément inventory_hostname ». On doit en effet définir dans les deux cas une liste pour que la boucle fonctionne.

Dans ce genre de situation, soit on privilégie la lisibilité, soit on rajoute plein de commentaire pour ne pas oublier dans 3 mois.

En général, on utilise plutôt les if / else dans des templates, et on évite de les utiliser dans les tâches. Donc on va partir sur la solution plus verbeuse.

Connexion base de données en IPv6

On doit rajouter l'option socket_options dans la config de Mobilizon pour que la connexion puisse se faire en IPv6:

snippet.ruby
config :mobilizon, Mobilizon.Storage.Repo,
  adapter: Ecto.Adapters.Postgres,
  socket_options: [:inet6],

SSH agent

On configure le SSH agent pour qu'il arrête de nous embêter avec les clés SSH et les mots de passe dans tous les sens:

https://wiki.archlinux.org/title/SSH_keys#Start_ssh-agent_with_systemd_user

Il faut sans doute créer le dossier pour pouvoir y mettre la config:

mkdir -p ~/.config/systemd/user

On crée la config ssh-agent.service:

[Unit]
Description=SSH key agent

[Service]
Type=simple
Environment=SSH_AUTH_SOCK=%t/ssh-agent.socket
ExecStart=/usr/bin/ssh-agent -t 1h -D -a $SSH_AUTH_SOCK

[Install]
WantedBy=default.target

Avec l'option -t on peut définir la durée de validité des clés SSH, c'est pratique pour ne pas avoir une dizaine de clés actives à la fin de la journée.

Lorsque la config a été créée, on fait:

systemctl start --user ssh-agent
systemctl enable --user ssh-agent

Dans la config ~/.bashrc ou ~/.zshrc, on rajoute:

export SSH_AUTH_SOCK="$XDG_RUNTIME_DIR/ssh-agent.socket"

Enfin, on ouvre un nouveau terminal et on vérifie que le ssh-agent tourne:

ssh-add -l

On rajoute également la commande magique dans ~/.ssh/config pour que SSH rajoute les clés qu'il utilise directement dans le SSH agent:

AddKeysToAgent  yes

Tests du playbook

On a pu lancer le playbook en local via molécule, maintenant on va le lancer sur la VM mobilizon.patata.louise.neutri.net qu'on avait créé le mois passé.

Ensuite, on lance manuellement un backup borgmatic, ceci afin de pouvoir tester la restauration des backups.

On vérifie qu'on a bien sauvegardé la clé Borg qui se trouve dans /root/.config/borg/keys.

On supprime la base de donnée 🙀 en se rendant sur le postgresql primaire. Ici, c'est pgsql-03.patata.louise.neutri.net. On fait un beau drop database:

sudo -u postgres dropdb mobilizon-neutrinet-be

Il nous dit que quelqu'un est en train d'utiliser la db, donc on stop Mobilizon et on recommence.

Ensuite, on se connecte au Proxmox pour repartir d'un snapshot de la VM Mobilizon.

Maintenant, on lance le playbook commun pour provisionner à nouveau la VM

Pour faire un restore des backups, voir la doc: https://torsion.org/borgmatic/docs/how-to/extract-a-backup/

~~Par contre, pour le moment on n'a pas encore la config borgmatic de Mobilizon… Il faut donc lancer le playbook pour au moins avoir cette config:~~

ansible-playbook playbooks/apps/mobilizon.yml -t borgmatic_config -l mobilizon --diff

En fait on n'a pas besoin de cette étape 🙂

~~Lorsque c'est fait,~~ on peut restaurer les fichiers de Mobilizon:

borgmatic extract -c /etc/borgmatic.d/common.yml --archive latest --destination / --progress

On doit préciser où le backup doit être restaurer (sinon il le fait par défaut à l'endroit où on lance la commande). On peut ajouter le flag --progress pour que borg indique ce qu'il fait et où il en est.

Il faut encore restaurer la db postgresql, le dump se trouve dans /root/.borgmatic/postgresql_databases.

Il y a une commande magique de borgmatic pour faire ça, mais malheureusement il faut que le client postgresql soit installé (ou du monis le paquet pg_restore). On va d'abord le faire manuellement sur pgsql-03, et on testera la commande magique plus tard.

On crée la base de données:

sudo -u postgres createdb mobilizon-neutrinet-be

Ensuite on restaure les données:

sudo -u postgres pg_restore -d mobilizon-neutrinet-be /tmp/mobilizon-neutrinet-be

Et enfin, on vérifie que les données sont bien là:

sudo -u postgres psql mobilizon-neutrinet-be`

On lance le playbook qui rajoute Caddy et le client Posgresql mais ne fait pas grand chose d'autre à part changer le masque sur /var/www.

Par contre, on remarque qu'on a des problèmes de permissions sur les fichiers de Mobilizon (ex: /var/www/mobilizon-neutrinet-be appartient à root).

En fait, c'est parce que borgmatic restaure les fichiers avant la création de l'utilisateur mobilizon (puisque celui-ci n'a pas encore été créé avec le playbook). Le mieux est donc de lancer d'abord le playbook puis de restaurer les données via borgmatic.

Néanmoins, il semble que même lorsque pg_restore (lié au client postgresql) est installé, la restauration de la DB via borg ne fonctionnent pas. Ce n'est pas grave puisqu'on peut le faire à la main.

Geolix

Il y a un machin, ça s'appelle Geolix mais on sait pas trop à quoi ça sert :

https://hexdocs.pm/geolix/Geolix.html

Au passage, on découvre la doc des CÉMÉA qui a l'air super complète \o/

https://ladoc.cemea.org/mallette/mobilizon

Cela permet de montrer des évènements en se basant sur la localisation de l'IP. En fait, c'est à la fin de la doc de mobilizon : https://docs.joinmobilizon.org/administration/install/release/#geolocation-databases

Donc si l'IP est sur Bruxelles, on verra tous les évènements de Neutrinet en premier.

On doit juste télécharger la base de données: https://download.db-ip.com/free/dbip-city-lite-2023-06.mmdb.gz

Et la décompresser.

Il y a moyen de mettre en place un cronjob pour mettre à jour cette base de données (voir doc des CÉMÉA), mais on le fera plus tard…

TODO: Mettre en place un cronjob avec l'outil geoipupdate (peut se faire à la main dans un premier temps)

Préparation de la migration

On va devoir récupérer un dump de la base de données de mobilizon.be

Il nous faudra aussi les médias (uploads, etc.), qui se trouvent dans /home/mobilizon/live/uploads.

TODO:

Gopass

Prochaine réunion

TODO: Mettre en place un cronjob avec l'outil geoipupdate (peut se faire à la main dans un premier temps)

Prochain atelier Ansible: On attend le transfert du nom de domaine

Lieu : Chez Célo

Météo de fin

Moment informel durant lequel on exprime en peu de mots comment, à titre personnel, la réunion a été vécue que ce soit positif ou négatif. Si une ou plusieurs tension est née durant la réunion, il est peut-être nécessaire d'envisager l'une ou l'autre réunion pour y remédier.