Bonjour,
Après avoir hésité pendant des années, je me penche sur ansible (j'ai ~50 conteneurs lxc sur 4 hosts, tout en debian, ça bouge très peu et je gérais ça à la main avec des scripts chargés d'harmoniser).
J'y vais doucement car j'ai de l'existant hétérogène et il s'agirait pas qu'une nouvelle task ansible vienne casser un truc qui marche.
J'y vais donc progressivement, à partir d'une organisation basée sur les roles, j'ajoute des tasks, je les teste avec du --check puis sans, d'abord sur peu de vm.
Si y'a une stratégie meilleure qu'une autre je suis preneur de tout conseil.
Concernant les variables j'ai lu pas mal de doc dont https://docs.ansible.com/ansible/2.9/user_guide/playbooks_variables.html https://docs.ansible.com/ansible/2.9/user_guide/playbooks_vars_facts.html https://docs.ansible.com/ansible/2.9/user_guide/playbooks_advanced_syntax.ht... https://docs.ansible.com/ansible/2.9/user_guide/complex_data_manipulation.ht... avec des filtres tout prêts pour gérer des ips https://docs.ansible.com/ansible/2.9/user_guide/playbooks_filters_ipaddr.htm...
puis les conditions https://docs.ansible.com/ansible/latest/user_guide/playbooks_conditionals.ht...
mais je patauge encore pas mal, par ex avec
group_vars/all.yml : ipv4_private: "{{ ansible_all_ipv4_addresses | ipaddr('private') | first | mandatory }}" # attention, celle-là peut être undefined ipv4_public: "{{ ansible_all_ipv4_addresses | ipaddr('public') | first}}"
test.yml - name: essais de création de vars dynamiques hosts: mulet tasks: - name: show ipv4 private adress debug: msg: "On a trouvé l'ip privée {{ ipv4_private }}" when: ipv4_private is defined
- name: show ipv4 public adress debug: msg: "On a trouvé l'ip publique {{ ipv4_public }}" when: ipv4_public is defined
=> sur ce host qui n'a qu'une ip privée (et c'est normal), ça me l'affiche bien sur la 1re tâche mais ensuite ça plante avec
"msg": "The conditional check 'ipv4_public is defined' failed. The error was: error while evaluating conditional (ipv4_public is defined): No first item, sequence was empty.
Et là je vois pas pourquoi :-/
Une idée ?
Hello,
Tu mets du code (petit certes, mais du code) dans des champs qui ne contiennent que des variables.... moi j'éviterais, je ne le fais qu'uniquement lorsque je veux positionner deux variables avec la même valeur avec une sorte de variable bidon technique.
Pour moi tu présuppose que ta variable ansible_all_ipv4_addresses contient au moins une ip publique et du coup prendre la première même s'il n'y en a pas
J'essayerais avec le module set_fact
Avec qq du genre , pas testé hein:
- name: set ipv4 public address variable if at least one set_fact: ipv4_public: ansible_all_ipv4_addresses | ipaddr('public') | first when: ansible_all_iansible_all_ipv4_addresses | ipaddr('public') | length > 0
- name: show ipv4 public adress debug: msg: "On a trouvé l'ip publique {{ ipv4_public }}" when: ipv4_public is defined
Cordialement,
JYL
Le 26/11/2020 à 18:54, Daniel Caillibaud a écrit :
Bonjour,
Après avoir hésité pendant des années, je me penche sur ansible (j'ai ~50 conteneurs lxc sur 4 hosts, tout en debian, ça bouge très peu et je gérais ça à la main avec des scripts chargés d'harmoniser).
J'y vais doucement car j'ai de l'existant hétérogène et il s'agirait pas qu'une nouvelle task ansible vienne casser un truc qui marche.
J'y vais donc progressivement, à partir d'une organisation basée sur les roles, j'ajoute des tasks, je les teste avec du --check puis sans, d'abord sur peu de vm.
Si y'a une stratégie meilleure qu'une autre je suis preneur de tout conseil.
Concernant les variables j'ai lu pas mal de doc dont https://docs.ansible.com/ansible/2.9/user_guide/playbooks_variables.html https://docs.ansible.com/ansible/2.9/user_guide/playbooks_vars_facts.html https://docs.ansible.com/ansible/2.9/user_guide/playbooks_advanced_syntax.ht... https://docs.ansible.com/ansible/2.9/user_guide/complex_data_manipulation.ht... avec des filtres tout prêts pour gérer des ips https://docs.ansible.com/ansible/2.9/user_guide/playbooks_filters_ipaddr.htm...
puis les conditions https://docs.ansible.com/ansible/latest/user_guide/playbooks_conditionals.ht...
mais je patauge encore pas mal, par ex avec
group_vars/all.yml : ipv4_private: "{{ ansible_all_ipv4_addresses | ipaddr('private') | first | mandatory }}" # attention, celle-là peut être undefined ipv4_public: "{{ ansible_all_ipv4_addresses | ipaddr('public') | first}}"
test.yml
name: essais de création de vars dynamiques hosts: mulet tasks:
name: show ipv4 private adress debug: msg: "On a trouvé l'ip privée {{ ipv4_private }}" when: ipv4_private is defined
name: show ipv4 public adress debug: msg: "On a trouvé l'ip publique {{ ipv4_public }}" when: ipv4_public is defined
=> sur ce host qui n'a qu'une ip privée (et c'est normal), ça me l'affiche bien sur la 1re tâche mais ensuite ça plante avec
"msg": "The conditional check 'ipv4_public is defined' failed. The error was: error while evaluating conditional (ipv4_public is defined): No first item, sequence was empty.
Et là je vois pas pourquoi :-/
Une idée ?
Le 26/11/20 à 19h28, Jean-Yves LENHOF jean-yves@lenhof.eu.org a écrit :
Hello,
Tu mets du code (petit certes, mais du code) dans des champs qui ne contiennent que des variables.... moi j'éviterais, je ne le fais qu'uniquement lorsque je veux positionner deux variables avec la même valeur avec une sorte de variable bidon technique.
Ok, tu veux dire que je devrais pas mettre de logique dans mon group_vars/all.yaml ?
Pour moi tu présuppose que ta variable ansible_all_ipv4_addresses contient au moins une ip publique et du coup prendre la première même s'il n'y en a pas
Ben, en fait en mettant dans group_vars/all.yaml ipv4_private: "{{ ansible_all_ipv4_addresses | ipaddr('private') | first | mandatory }}" ipv4_public: "{{ ansible_all_ipv4_addresses | ipaddr('public') | first }}"
Je voulais - que ça plante si y'a pas d'ip privée (car ça devrait jamais arriver, pas la peine d'aller plus loin dans ce cas) - que ipv4_public soit initialisé avec la 1re ip publique si y'en a une, et undefined sinon pour n'importe quel playbook qui pourrait être lancé.
J'essayerais avec le module set_fact
Ok, mais lui je dois le mettre dans une task, ce qui m'intéressait c'était d'initialiser mes variables avant la 1re task, quel que soit le playbook
Comment je pourrais utiliser un set_fact avant toute tâche ?
En tout cas tu as raison, c'est le |first sur un truc inexistant qui lui plaît pas, mais pas au moment de l'évaluer, juste lorsqu'il traite ce résultat. J'avais pas compris ça dans le message d'erreur, car le message sort après le print ok de l'ip privée, je pensais donc que mes définitions avaient déjà été évaluées et ne lui avait pas posé pb.
Si je remplace dans mes vars mon ipv4_public: "{{ ansible_all_ipv4_addresses | ipaddr('public') | first }}" par ipv4_public: "{{ ansible_all_ipv4_addresses | ipaddr('public') | default([]) | first | default('') }}" ou même ipv4_public: "{{ ansible_all_ipv4_addresses | ipaddr('public') | first | default('') }}"
et ensuite dans les tasks le when: ipv4_public is defined par when: ipv4_public is defined and ipv4_public | bool ou when: ipv4_public | bool
ça semble fonctionner comme je voulais.
Le "is defined" est superflu, car maintenant c'est toujours defined (éventuellement vide), j'hésite à le laisser au cas où la définition des variables changerait, c'est probablement idiot (pas la peine de laisser le choix, autant planter tout de suite si la variable est pas définie puisqu'elle est toujours sensée l'être avec cette nouvelle définition).
Avec qq du genre , pas testé hein:
- name: set ipv4 public address variable if at least one set_fact: ipv4_public: ansible_all_ipv4_addresses | ipaddr('public') |
first when: ansible_all_iansible_all_ipv4_addresses | ipaddr('public') | length > 0
- name: show ipv4 public adress debug: msg: "On a trouvé l'ip publique {{ ipv4_public }}" when: ipv4_public is defined
Je vois bien l'idée, merci pour avoir pointé le pb au bon endroit.
Ce que j'ai pas pigé c'est ce que renvoie ipaddr('public') quand y'en a pas (c'est pas un tableau vide), et ce que renvoie | first quand on lui file la sortie du précédent (c'est pas undefined)
Faudrait probablement que je me mette à python pour comprendre…
Bonjour, ça fait un moment que je travaille avec des outils de gestion de déploiement et de gestion de configuration (puppet, saltstack et maintenant ansible).
Mon expérience est que quand ça devient trop compliqué, l'approche est souvent mauvaise (c'est d'ailleurs souvent vrai pour d'autres aspects de l'informatique).
Voici quelques recommandations que j'essaie de garder en tête au moment de développer des nouveaux rôles/tasks, peut-être qu'elles pourront te servir : - éviter au maximum de mettre de l'intelligence dans des rôles (sinon ça devient rapidement plus portable et c'est le début de la complexité inutile). - privilégier le plus possible une approche descriptive de l'infra (dans group_vars/host_vars), cela simplifie énormément les développements et la lisibilité des rôles. Pourquoi vouloir détecter des IP qui ne changent pas ? - utiliser le plus possible des variables raw, ça permet de simplifier drastiquement les templates ; - essayer d'éviter les `| default("")` et passer par `defaults/main.yml` pour documenter la valeur par défaut des variables (et documenter le rôle finalement).
En ce qui concerne Ansible, son problème principal selon moi c'est sa première qualité. C'est extrêmement facile à utiliser (notamment, car il est agentless et n'a besoin que d'une connexion SSH). Du coup on se lance tête baissée dans des déploiements/développements sans commencer par structurer les choses proprement.
En ce qui concerne l'utilisation du `--check`, c'est évidemment très bien mais je t'invite à regarder le projet molecule qui permet de tester rapidement et proprement les rôles, ça fait gagner un temps fou pendant la phase de développement (même si le setup est un peu plus long).
Si tu lis l'anglais, je te conseille ce livre (et les productions en général de son auteur) : https://www.ansiblefordevops.com/
Finalement, voici un rôle que j'ai développé qui illustre ce qui me semble être des bonnes pratiques pour ansible (il n'est surement pas parfait mais je crois qu'il est assez simple à lire). Ce rôle contient également les mécanismes de tests molecule dont je parle plus haut. https://github.com/fauust/ansible-role-mariadb
Mes 2 centimes.
Faustin
PS : pour ceux qui trouvent ansible un peu long (et quand on vient de saltstack, on pleure), je vous recommande d'essayer mitogen (https://mitogen.networkgenomics.com/ansible_detailed.html), le gain en performance est assez bluffant.
Daniel Caillibaud ml@lairdutemps.org, 27/11/2020 – 03:51:43 (+0100):
Le 26/11/20 à 19h28, Jean-Yves LENHOF jean-yves@lenhof.eu.org a écrit :
Hello,
Tu mets du code (petit certes, mais du code) dans des champs qui ne contiennent que des variables.... moi j'éviterais, je ne le fais qu'uniquement lorsque je veux positionner deux variables avec la même valeur avec une sorte de variable bidon technique.
Ok, tu veux dire que je devrais pas mettre de logique dans mon group_vars/all.yaml ?
Pour moi tu présuppose que ta variable ansible_all_ipv4_addresses contient au moins une ip publique et du coup prendre la première même s'il n'y en a pas
Ben, en fait en mettant dans group_vars/all.yaml ipv4_private: "{{ ansible_all_ipv4_addresses | ipaddr('private') | first | mandatory }}" ipv4_public: "{{ ansible_all_ipv4_addresses | ipaddr('public') | first }}"
Je voulais
- que ça plante si y'a pas d'ip privée (car ça devrait jamais arriver, pas la peine d'aller plus loin dans ce cas)
- que ipv4_public soit initialisé avec la 1re ip publique si y'en a une, et undefined sinon
pour n'importe quel playbook qui pourrait être lancé.
J'essayerais avec le module set_fact
Ok, mais lui je dois le mettre dans une task, ce qui m'intéressait c'était d'initialiser mes variables avant la 1re task, quel que soit le playbook
Comment je pourrais utiliser un set_fact avant toute tâche ?
En tout cas tu as raison, c'est le |first sur un truc inexistant qui lui plaît pas, mais pas au moment de l'évaluer, juste lorsqu'il traite ce résultat. J'avais pas compris ça dans le message d'erreur, car le message sort après le print ok de l'ip privée, je pensais donc que mes définitions avaient déjà été évaluées et ne lui avait pas posé pb.
Si je remplace dans mes vars mon ipv4_public: "{{ ansible_all_ipv4_addresses | ipaddr('public') | first }}" par ipv4_public: "{{ ansible_all_ipv4_addresses | ipaddr('public') | default([]) | first | default('') }}" ou même ipv4_public: "{{ ansible_all_ipv4_addresses | ipaddr('public') | first | default('') }}"
et ensuite dans les tasks le when: ipv4_public is defined par when: ipv4_public is defined and ipv4_public | bool ou when: ipv4_public | bool
ça semble fonctionner comme je voulais.
Le "is defined" est superflu, car maintenant c'est toujours defined (éventuellement vide), j'hésite à le laisser au cas où la définition des variables changerait, c'est probablement idiot (pas la peine de laisser le choix, autant planter tout de suite si la variable est pas définie puisqu'elle est toujours sensée l'être avec cette nouvelle définition).
Avec qq du genre , pas testé hein:
- name: set ipv4 public address variable if at least one set_fact: ipv4_public: ansible_all_ipv4_addresses | ipaddr('public') |
first when: ansible_all_iansible_all_ipv4_addresses | ipaddr('public') | length > 0
- name: show ipv4 public adress debug: msg: "On a trouvé l'ip publique {{ ipv4_public }}" when: ipv4_public is defined
Je vois bien l'idée, merci pour avoir pointé le pb au bon endroit.
Ce que j'ai pas pigé c'est ce que renvoie ipaddr('public') quand y'en a pas (c'est pas un tableau vide), et ce que renvoie | first quand on lui file la sortie du précédent (c'est pas undefined)
Faudrait probablement que je me mette à python pour comprendre…
Le 27/11/20 à 12h16, Faustin Lammler faustin@fala.red a écrit :
Bonjour, ça fait un moment que je travaille avec des outils de gestion de déploiement et de gestion de configuration (puppet, saltstack et maintenant ansible).
Mon expérience est que quand ça devient trop compliqué, l'approche est souvent mauvaise (c'est d'ailleurs souvent vrai pour d'autres aspects de l'informatique).
Ça je suis bien d'accord, stay kiss !
Voici quelques recommandations que j'essaie de garder en tête au moment de développer des nouveaux rôles/tasks, peut-être qu'elles pourront te servir :
- éviter au maximum de mettre de l'intelligence dans des rôles (sinon ça devient rapidement plus portable et c'est le début de la complexité inutile).
- privilégier le plus possible une approche descriptive de l'infra (dans group_vars/host_vars), cela simplifie énormément les développements et la lisibilité des rôles. Pourquoi vouloir détecter des IP qui ne changent pas ?
Mmmh, je vois, mais vu que ces ips sont déjà dans une zone locale de mes résolveurs (qui est générée à partir d'un fichier unique qui liste host=>ip), je voulais pas mettre l'association host/ip à deux endroits, toujours un risque d'oublier de màj un des deux.
Du coup, je devrais peut-être décrire cette liste host/ip dans ansible et utiliser ansible pour générer la zone unbound.
- utiliser le plus possible des variables raw, ça permet de simplifier drastiquement les templates ;
- essayer d'éviter les `| default("")` et passer par `defaults/main.yml` pour documenter la valeur par défaut des variables (et documenter le rôle finalement).
Vu, merci.
En ce qui concerne Ansible, son problème principal selon moi c'est sa première qualité. C'est extrêmement facile à utiliser (notamment, car il est agentless et n'a besoin que d'une connexion SSH). Du coup on se lance tête baissée dans des déploiements/développements sans commencer par structurer les choses proprement.
En ce qui concerne l'utilisation du `--check`, c'est évidemment très bien mais je t'invite à regarder le projet molecule qui permet de tester rapidement et proprement les rôles, ça fait gagner un temps fou pendant la phase de développement (même si le setup est un peu plus long).
Si tu lis l'anglais, je te conseille ce livre (et les productions en général de son auteur) : https://www.ansiblefordevops.com/
Finalement, voici un rôle que j'ai développé qui illustre ce qui me semble être des bonnes pratiques pour ansible (il n'est surement pas parfait mais je crois qu'il est assez simple à lire). Ce rôle contient également les mécanismes de tests molecule dont je parle plus haut. https://github.com/fauust/ansible-role-mariadb
Mes 2 centimes.
Faustin
PS : pour ceux qui trouvent ansible un peu long (et quand on vient de saltstack, on pleure), je vous recommande d'essayer mitogen (https://mitogen.networkgenomics.com/ansible_detailed.html), le gain en performance est assez bluffant.
Merci bcp pour tous ces conseils et pointeurs judicieux !
Daniel Caillibaud ml@lairdutemps.org, 27/11/2020 – 14:01:46 (+0100):
Mmmh, je vois, mais vu que ces ips sont déjà dans une zone locale de mes résolveurs (qui est générée à partir d'un fichier unique qui liste host=>ip), je voulais pas mettre l'association host/ip à deux endroits, toujours un risque d'oublier de màj un des deux.
Oui, dupliquer les infos c'est la meilleure façon d'en oublier un bout.
Du coup, je devrais peut-être décrire cette liste host/ip dans ansible et utiliser ansible pour générer la zone unbound.
Oui par exemple, ou alors regarder du côté des inventory dynamiques : https://docs.ansible.com/ansible/latest/user_guide/intro_dynamic_inventory.h...
Bonsoir,
C'est parce qu'il essaie de résoudre la variable dans la condition, et que le first ne fonctionne pas sur une liste vide.
Essaie d'ajouter | default([""]) avant le first variable, s'il n'y a pas d'adresse IP publique il devrait prendre cette valeur par défaut et t'afficher un string vide. Il faudrait que tu modifies aussi ta condition pour être si différent de "" du coup ou de longueur > 1. Rémi CAZENAVE - SCOP Le Filament 06.87.23.26.04
-------- Courriel d’origine -------- De : Daniel Caillibaud ml@lairdutemps.org Envoyé : 26 novembre 2020 18:54:08 GMT+01:00 À : "'frsag@frsag.org'" frsag@frsag.org Objet : [FRsAG] Pb de vars ansible
Bonjour,
Après avoir hésité pendant des années, je me penche sur ansible (j'ai ~50 conteneurs lxc sur 4 hosts, tout en debian, ça bouge très peu et je gérais ça à la main avec des scripts chargés d'harmoniser).
J'y vais doucement car j'ai de l'existant hétérogène et il s'agirait pas qu'une nouvelle task ansible vienne casser un truc qui marche.
J'y vais donc progressivement, à partir d'une organisation basée sur les roles, j'ajoute des tasks, je les teste avec du --check puis sans, d'abord sur peu de vm.
Si y'a une stratégie meilleure qu'une autre je suis preneur de tout conseil.
Concernant les variables j'ai lu pas mal de doc dont https://docs.ansible.com/ansible/2.9/user_guide/playbooks_variables.html https://docs.ansible.com/ansible/2.9/user_guide/playbooks_vars_facts.html https://docs.ansible.com/ansible/2.9/user_guide/playbooks_advanced_syntax.ht... https://docs.ansible.com/ansible/2.9/user_guide/complex_data_manipulation.ht... avec des filtres tout prêts pour gérer des ips https://docs.ansible.com/ansible/2.9/user_guide/playbooks_filters_ipaddr.htm...
puis les conditions https://docs.ansible.com/ansible/latest/user_guide/playbooks_conditionals.ht...
mais je patauge encore pas mal, par ex avec
group_vars/all.yml : ipv4_private: "{{ ansible_all_ipv4_addresses | ipaddr('private') | first | mandatory }}" # attention, celle-là peut être undefined ipv4_public: "{{ ansible_all_ipv4_addresses | ipaddr('public') | first}}"
test.yml - name: essais de création de vars dynamiques hosts: mulet tasks: - name: show ipv4 private adress debug: msg: "On a trouvé l'ip privée {{ ipv4_private }}" when: ipv4_private is defined
- name: show ipv4 public adress debug: msg: "On a trouvé l'ip publique {{ ipv4_public }}" when: ipv4_public is defined
=> sur ce host qui n'a qu'une ip privée (et c'est normal), ça me l'affiche bien sur la 1re tâche mais ensuite ça plante avec
"msg": "The conditional check 'ipv4_public is defined' failed. The error was: error while evaluating conditional (ipv4_public is defined): No first item, sequence was empty.
Et là je vois pas pourquoi :-/
Une idée ?
Le 26/11/20 à 21h17, Rémi CAZENAVE - Le Filament remi@le-filament.com a écrit :
Bonsoir,
C'est parce qu'il essaie de résoudre la variable dans la condition, et que le first ne fonctionne pas sur une liste vide.
En fait je m'attendais à ce que ce first sur une liste vide rende la variable undefined.
Si je teste avec debug: msg: "Type du []|first : {{ ansible_all_ipv4_addresses | ipaddr('public') | first | type_debug }} "
ça affiche AnsibleUndefined
mais l'affecter à une variable et tester ensuite avec "is defined" marche pas, je sais pas trop pourquoi… (et pas vu comment tester le type).
Essaie d'ajouter | default([""]) avant le first variable, s'il n'y a pas d'adresse IP publique il devrait prendre cette valeur par défaut et t'afficher un string vide. Il faudrait que tu modifies aussi ta condition pour être si différent de "" du coup ou de longueur > 1
Oui, c'est la solution de contournement (ou comme le précise Faustin mettre ça dans un defaults/main.yml)
=> toutes les variables sont toujours définies mais parfois vides, et on teste juste si c'est truthy avec du
when: foo|bool