web-dev-qa-db-fra.com

Comment unifier les tâches d'installation des packages dans Ansible?

Je commence par ansible et je l'utiliserai, entre autres, pour installer des packages sur plusieurs distributions Linux.

Je vois dans la documentation que les commandes yum et apt sont séparées - quelle serait la façon la plus simple de les unifier et d'utiliser quelque chose comme ceci:

- name: install the latest version of Apache
  unified_install: name=httpd state=latest

au lieu de

- name: install the latest version of Apache on CentOS
  yum: name=httpd state=latest
  when: ansible_os_family == "RedHat"

- name: install the latest version of Apache on Debian
  apt: pkg=httpd state=latest 
  when: ansible_os_family == "Debian"

Je comprends que les deux gestionnaires de packages sont différents, mais ils ont toujours un ensemble d'utilisations de base communes. D'autres orchestrateurs ( sel par exemple ) ont une seule commande d'installation.

71
WoJ

Mise à jour: depuis Ansible 2.0, il existe désormais un module générique et abstrait package module

Exemples d'utilisation:

Maintenant, lorsque le nom du package est le même dans différentes familles de systèmes d'exploitation, c'est aussi simple que:

---
- name: Install foo
  package: name=foo state=latest

Lorsque le nom du package diffère selon les familles de systèmes d'exploitation, vous pouvez le gérer avec des fichiers vars spécifiques à la distribution ou à la famille de systèmes d'exploitation:

---
# roles/Apache/apache.yml: Tasks entry point for 'Apache' role. Called by main.yml
# Load a variable file based on the OS type, or a default if not found.
- include_vars: "{{ item }}"
  with_first_found:
    - "../vars/{{ ansible_distribution }}-{{ ansible_distribution_major_version | int}}.yml"
    - "../vars/{{ ansible_distribution }}.yml"
    - "../vars/{{ ansible_os_family }}.yml"
    - "../vars/default.yml"
  when: Apache_package_name is not defined or Apache_service_name is not defined

- name: Install Apache
  package: >
    name={{ Apache_package_name }}
    state=latest

- name: Enable Apache service
  service: >
    name={{ Apache_service_name }}
    state=started
    enabled=yes
  tags: packages

Ensuite, pour chaque OS que vous devez gérer différemment ... créez un fichier vars:

---
# roles/Apache/vars/default.yml
Apache_package_name: Apache2
Apache_service_name: Apache2

---
# roles/Apache/vars/RedHat.yml
Apache_package_name: httpd
Apache_service_name: httpd

---
# roles/Apache/vars/SLES.yml
Apache_package_name: Apache2
Apache_service_name: Apache2

---
# roles/Apache/vars/Debian.yml
Apache_package_name: Apache2
Apache_service_name: Apache2

---
# roles/Apache/vars/Archlinux.yml
Apache_package_name: Apache
apache_service_name: httpd



MODIFIER:  Depuis Michael DeHaan (créateur d'Ansible) a choisi de ne pas faire abstraction des modules du gestionnaire de paquets comme Chef le fait,

Si vous utilisez toujours une ancienne version d'Ansible (Ansible <2.0), malheureusement, vous devrez gérer faire cela dans tous de vos playbooks et rôles. [~ # ~] imho [~ # ~] cela pousse beaucoup de travaux répétitifs inutiles sur les auteurs de livres et de rôles ... mais c'est comme ça actuellement. Notez que je ne dis pas que nous devrions essayer d'abstraire les gestionnaires de packages tout en essayant de prendre en charge toutes leurs options et commandes spécifiques, mais simplement d'avoir un moyen facile d'installer un package qui est indépendant du gestionnaire de packages. Je ne dis pas non plus que nous devrions tous sauter sur le mouvement Smart Package Manager , mais qu'une sorte de couche d'abstraction d'installation de package dans votre outil de gestion de la configuration est très utile pour simplifier les livres de lecture/livres de recettes multiplateformes. . Le projet Smart semble intéressant, mais il est assez ambitieux d'unifier la gestion des packages sur les distributions et les plates-formes sans beaucoup d'adoption pour le moment ... il sera intéressant de voir s'il réussit. Le vrai problème est simplement que les noms de packages ont parfois tendance à être différents d'une distribution à l'autre, nous devons donc toujours faire des déclarations de cas ou when: instructions pour gérer les différences.

La façon dont je l'ai traité est de suivre cette structure de répertoire tasks dans un playbook ou un rôle:

roles/foo
└── tasks
    ├── apt_package.yml
    ├── foo.yml
    ├── homebrew_package.yml
    ├── main.yml
    └── yum_package.yml

Et puis avoir ceci dans mon main.yml:

---
# foo: entry point for tasks
#                 Generally only include other file(s) and add tags here.

- include: foo.yml tags=foo

En ce foo.yml (pour le paquet 'foo'):

---
# foo: Tasks entry point. Called by main.yml
- include: apt_package.yml
  when: ansible_pkg_mgr == 'apt'
- include: yum_package.yml
  when: ansible_pkg_mgr == 'yum'
- include: homebrew_package.yml
  when: ansible_os_family == 'Darwin'

- name: Enable foo service
  service: >
    name=foo
    state=started
    enabled=yes
  tags: packages
  when: ansible_os_family != 'Darwin'

Ensuite pour les différents gestionnaires de packages:

App:

---
# tasks file for installing foo on apt based distros

- name: Install foo package via apt
  apt: >
    name=foo{% if foo_version is defined %}={{ foo_version }}{% endif %}
    state={% if foo_install_latest is defined and foo_version is not defined %}latest{% else %}present{% endif %}
  tags: packages

Miam:

---
# tasks file for installing foo on yum based distros
- name: Install EPEL 6.8 repos (...because it's RedHat and foo is in EPEL for example purposes...)
  yum: >
    name={{ docker_yum_repo_url }}
    state=present
  tags: packages
  when: ansible_os_family == "RedHat" and ansible_distribution_major_version|int == 6

- name: Install foo package via yum
  yum: >
    name=foo{% if foo_version is defined %}-{{ foo_version }}{% endif %}
    state={% if foo_install_latest is defined and foo_version is not defined %}latest{% else %}present{% endif %}
  tags: packages

- name: Install RedHat/yum-based distro specific stuff...
  yum: >
    name=some-other-custom-dependency-on-redhat
    state=latest
  when: ansible_os_family == "RedHat"
  tags: packages

Homebrew:

---
- name: Tap homebrew foobar/foo
  homebrew_tap: >
    name=foobar/foo
    state=present

- homebrew: >
    name=foo
    state=latest

Notez que cela est terriblement répétitif et non DRY , et bien que certaines choses pourraient être différentes sur les différentes plates-formes et devront être gérées, généralement je pense que c'est verbeux et lourd par rapport à Chef:

package 'foo' do
  version node['foo']['version']
end

case node["platform"]
when "debian", "ubuntu"
  # do debian/ubuntu things
when "redhat", "centos", "Fedora"
  # do redhat/centos/Fedora things
end

Et oui, il y a l'argument selon lequel certains les noms de package sont différents d'une distribution à l'autre. Et bien qu'il existe actuellement un manque de données facilement accessibles , je me risquerais à deviner que la plupart des noms de paquets populaires sont communs à travers les distributions et pourrait être installé via un module de gestionnaire de paquets abstrait. Les cas spéciaux devraient de toute façon être traités et nécessiteraient déjà un travail supplémentaire, ce qui réduirait les risques de D.R.Y. En cas de doute, vérifiez pkgs.org .

69
TrinitronX

Vous pouvez résumer les gestionnaires de packages via des faits

- name: Install packages
  with_items: package_list
  action: "{{ ansible_pkg_mgr }} state=installed name={{ item }}"

Tout ce dont vous avez besoin est d'une logique qui définit ansible_pkg_mgr à apt ou yum etc.

Ansible travaille également à faire ce que vous voulez dans un futur module .

13
xddsg

Depuis Ansible 2.0, il y a le nouveau module Package-

http://docs.ansible.com/ansible/package_module.html

Vous pouvez ensuite l'utiliser comme votre proposition:

- name: install the latest version of Apache
  package: name=httpd state=latest

Vous devez toujours tenir compte des différences de nom.

6
Tvartom

Consultez la documentation d'Ansible sur Importations conditionnelles .

Une tâche pour vous assurer qu'Apache est en cours d'exécution même si les noms de service sont différents sur chaque système d'exploitation.

---
- hosts: all
  remote_user: root
  vars_files:
    - "vars/common.yml"
    - [ "vars/{{ ansible_os_family }}.yml", "vars/os_defaults.yml" ]
  tasks:
  - name: make sure Apache is running
    service: name={{ Apache }} state=running
3
David Vasandani

Vous ne voulez pas le faire car certains noms de packages diffèrent entre les distributions. Par exemple, sur les distributions liées à RHEL, le package de serveur Web populaire est nommé httpd, tandis que, comme sur les distributions liées à Debian, il est nommé Apache2. De même avec une énorme liste d'autres systèmes et bibliothèques de support.

Il peut y avoir un ensemble de paramètres de base communs, mais il y a aussi un certain nombre de paramètres plus avancés qui sont différents entre les gestionnaires de packages. Et vous ne voulez pas être dans une situation ambiguë où pour certaines commandes vous utilisez une syntaxe et pour d'autres commandes vous utilisez une autre syntaxe.

2
Mxx