Wie vereinheitlicht man Paketinstallationsaufgaben in Ansible?

68

Ich beginne mit ansible und werde es unter anderem verwenden, um Pakete auf mehreren Linux-Distributionen zu installieren.

Ich sehe in den Dokumenten, dass die Befehle yumund aptgetrennt sind - was wäre der einfachste Weg, sie zu vereinheitlichen und so etwas zu benutzen:

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

Anstatt von

- 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"

Ich verstehe, dass die beiden Paketmanager unterschiedlich sind, aber immer noch eine Reihe gemeinsamer grundlegender Verwendungen haben. Andere Orchestatoren ( z. B. Salt ) verfügen über einen einzigen Installationsbefehl.

WoJ
quelle
Sie könnten drei Rezepte haben: eines, das über eine gemeinsame Liste iteriert, und eines, das sich jeweils auf betriebssystemspezifische Listen bezieht. Ich versuche jetzt herauszufinden, wie ein Handler mit einem betriebssystemspezifischen Dienstnamen benachrichtigt wird, nachdem ein gemeinsames Konfigurationselement festgelegt wurde. Viel Glück!
Dannyman

Antworten:

66

Update: Ab Ansible 2.0 gibt es jetzt ein generisches & abstrahiertes packageModul

Anwendungsbeispiele:

Wenn der Paketname für verschiedene Betriebssystemfamilien gleich ist, ist dies so einfach wie folgt:

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

Wenn sich der Paketname in den Betriebssystemfamilien unterscheidet, können Sie ihn mit verteilungs- oder betriebssystemfamilienspezifischen vars-Dateien verarbeiten:

---
# 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

Erstellen Sie dann für jedes Betriebssystem, mit dem Sie anders umgehen müssen, eine vars-Datei:

---
# 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



BEARBEITEN: Da Michael DeHaan (der Schöpfer von Ansible) sich dafür entschieden hat, die Module des Paketmanagers nicht zu abstrahieren, wie es Chef tut,

Wenn Sie noch eine ältere Version von Ansible (Ansible <2.0) verwenden , müssen Sie dies leider in all Ihren Playbooks und Rollenerledigen. IMHO, dies bringt eine Menge unnötiger sich wiederholender Arbeit auf die Autoren von Spielbüchern und Rollen ... aber es ist so, wie es derzeit ist. Beachten Sie, dass wir nicht versuchen sollten, Paketmanager zu abstrahieren, während wir weiterhin versuchen, alle ihre spezifischen Optionen und Befehle zu unterstützen, sondern nur einen einfachen Weg haben, ein Paket zu installieren, das paketmanager-unabhängig ist. Ich sage auch nicht, dass wir alle auf den Smart Package Manager umsteigen solltenDiese Art von Paketinstallations-Abstraktionsschicht in Ihrem Konfigurationsmanagement-Tool ist jedoch sehr nützlich, um plattformübergreifende Playbooks / Kochbücher zu vereinfachen. Das Smart-Projekt sieht interessant aus, aber es ist ziemlich ehrgeizig, das Paketmanagement über Distributionen und Plattformen hinweg zu vereinheitlichen, ohne dass es viel Akzeptanz findet. Es wird interessant sein zu sehen, ob es erfolgreich ist. Das eigentliche Problem ist nur, dass die Paketnamen manchmal in verschiedenen Distributionen unterschiedlich sind. Wir müssen also noch case-Anweisungen oder when:Anweisungen ausführen, um die Unterschiede zu behandeln.

Die Art und Weise, wie ich damit umgegangen bin, besteht darin, dieser tasksVerzeichnisstruktur in einem Playbook oder einer Rolle zu folgen :

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

Und dann haben Sie dies in meinem main.yml:

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

- include: foo.yml tags=foo

Dies in foo.yml(für Paket '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'

Dann für die verschiedenen Paketmanager:

Geeignet:

---
# 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

Lecker

---
# 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

Beachten Sie, dass diese schrecklich repetitive und nicht DRY , und obwohl einige Dinge vielleicht anders auf den verschiedenen Plattformen und werden gehandhabt werden müssen, in der Regel denke ich , das ausführlich und unhandlich ist , wenn zu Koch verglichen:

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

Und ja, es gibt das Argument, dass einige Paketnamen in Distributionen unterschiedlich sind. Und obwohl es derzeit an leicht zugänglichen Daten mangelt , würde ich vermuten, dass die beliebtesten Paketnamen in Distributionen häufig vorkommen und über ein abstrahiertes Paketmanagermodul installiert werden könnten. Sonderfälle müssten ohnehin behandelt werden und würden bereits zusätzliche Arbeit erfordern, um die Dinge weniger trocken zu machen .

TrinitronX
quelle
Mit Ansible 2 können Sie das Paketmodul verwenden, um
Guido
@ GuidoGarcía: Sehr schön! Hinzufügen eines Hinweises dazu für Ansible 2.0
TrinitronX
Vielleicht ist es auch erwähnenswert, dass Sie eine durch Kommas getrennte Liste oder nur eine Liste von Paketen angeben können.
Wes Turner
13

Sie können Paketmanager über Fakten abstrahieren

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

Alles, was Sie brauchen, ist eine Logik, die ansible_pkg_mgrauf aptoder yumusw. setzt .

Ansible arbeitet auch daran, in einem zukünftigen Modul das zu tun, was Sie wollen .

xddsg
quelle
1
Ansible richtet ansible_pkg_mgrsich an jeden bekannten Packager. Sie müssen nichts tun. Ich benutze dieses spezielle Konstrukt überall.
Michael Hampton
Die Syntax ist immer noch sehr nützlich für diejenigen, die den Ablauf ihrer Playbooks optimieren möchten. Das generische Paketmodul bietet noch keine Optimierung für with_items, daher ist es viel langsamer, wenn mehrere Pakete gleichzeitig installiert werden.
Danila Vershinin
@DanielV. Beachten Sie, dass das Github-Problem eine Problemumgehung bietet.
Michael Hampton
6

Ab Ansible 2.0 gibt es das neue Package-modul.

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

Sie können es dann wie Ihren Vorschlag verwenden:

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

Sie müssen noch Namensunterschiede berücksichtigen.

Tvartom
quelle
3

Lesen Sie die Ansible-Dokumentation zu bedingten Importen .

Eine Aufgabe, um sicherzustellen, dass Apache auch dann ausgeführt wird, wenn die Dienstnamen in den einzelnen Betriebssystemen unterschiedlich sind.

---
- 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
David Vasandani
quelle
2

Sie möchten dies nicht tun, da sich bestimmte Paketnamen zwischen Distributionen unterscheiden. Zum Beispiel wird auf RHEL-bezogenen Distributionen das beliebte Webserver-Paket genannt httpd, während es auf Debian-bezogenen Distributionen heißt apache2. Ähnlich verhält es sich mit einer riesigen Liste anderer System- und Unterstützungsbibliotheken.

Möglicherweise gibt es eine Reihe gemeinsamer Basisparameter, aber es gibt auch eine Reihe erweiterter Parameter, die sich zwischen den Paketmanagern unterscheiden. Und Sie möchten nicht in einer mehrdeutigen Situation sein, in der Sie für einige Befehle eine und für andere Befehle eine andere Syntax verwenden.

Mxx
quelle
Dies ist mehr oder weniger das, was ich erwartet hatte (leider :), also frage ich mich, wie es saltgelingt, beide Paketmanager zu vereinen. Jedenfalls werde ich dann auf eine doppelte Konfiguration zurückgreifen.
WoJ
Oder leiten Sie keinen Distributionszoo ;-) Migrieren Sie zu einer Infrastruktur mit nur einer Distribution und führen Sie ein glücklicheres Leben.
Mxx
Der Zoo ist zum Glück nur zwei Tiere groß, aber das ist die niedrigste Zahl, die ich erreichen kann :)
WoJ
1
@ Mxx, das ist eine gute Logik für einen Systemadministrator, aber was ist mit einem Softwareanbieter oder Berater, der mehrere Plattformen unterstützt?
David H. Bennett
@David, dann muss dies mit den Distributoren besprochen werden, damit sie einheitliche Paketnamen haben und Tools installieren können. Es gibt realistisch gesehen keine Möglichkeit, dass Ansible eine einheitliche Zuordnung von ALLEN Paketen aus allen unterstützten Distributionen aller Versionen erstellen kann.
Mxx