Schlagwort-Archiv: Ansible

Ansible – Die Module copy und cron

In diesem Beitrag zu meiner kleinen Ansible-Reihe beschäftige ich mich mit den Modulen copy[1. Ansible: copy – Copies files to remote locations] und cron[2. Ansible: cron – Manage cron.d and crontab entries].

Hinweis: Die folgende Konfiguration ist nicht zur Nachahmung empfohlen. Es handelt sich dabei um meine ersten Schritte mit Ansible. Es ist daher wahrscheinlich, dass die Konfiguration noch Fehler enthält, nicht optimiert ist und ein großes Verbesserungspotenzial besitzt. Die Nachahmung erfolgt ausdrücklich auf eigene Gefahr. Über Hinweise, wie man es besser machen kann, freue ich mich jederzeit.

Anforderungen

In diesem Szenario sollen Shell-Skripte auf das Zielsystem kopiert werden. Anschließend sind Einträge in der crontab zu erstellen, um diese Shell-Skripte auszuführen. Beim Zielsystem handelt es sich um die Gruppe [i-stage], in der sich aktuell nur ein Host namens host-i1.example.com befindet.

Vorbereitungen

Die zu verteilenden Skripte werden auf der Ansible-Control-Machine im Verzeichnis /root/src/ gesammelt. Anschließend wurde eine Rolle für dieses Vorhaben erstellt. Die auf meiner Spielwiese verwendete Verzeichnisstruktur wurde bereits in den vergangenen Beiträgen[3. Linux-Benutzerkonten mit Ansible verwalten] zu dieser Reihe beschrieben.

Umsetzung

Zur Umsetzung der Anforderungen wird ein Playbook erstellt, welches die geforderten Tasks ausführt. Der folgende Code-Block listet die benötigten Dateien inkl. ihres kommentierten Inhalts auf:

# Begin of file: local_scripts_i-stage.yml
---
- hosts: i-stage
  roles:
    - local_scripts
# End of file ############################

# Begin of file: roles/local_scripts/tasks/main.yml
---
## Copy local script files to target hosts
- copy: src=/root/src/sript1.sh dest=/root/src/sript1.sh owner=root group=root mode=755
- copy: src=/root/src/sript2.sh dest=/root/src/sript2.sh owner=root group=root mode=755

## Create cron jobs for local script files
- cron: name="local script1" minute="10" hour="1" job="/root/src/sript1.sh"
- cron: name="local script2" minute="12" hour="1" job="/root/src/sript2.sh"
# End of file #############################

Nach der Ausführung des Playbooks liegen die Skripte auf dem oder den Zielsystemen im gewünschten Verzeichnis /root/bin/. Lässt man sich die Crontab des Benutzers root anzeigen, so erkennt man die von Ansible hinzugefügten Einträge:

# crontab -l
# DO NOT EDIT THIS FILE - edit the master and reinstall.
# (/tmp/crontabaf5r8i installed on Mon Jul 18 11:52:09 2016)
# (Cronie version 4.2)
#Ansible: local script1
10 1 * * * /root/bin/script1.sh
#Ansible: local script2
12 1 * * * /root/bin/script2.sh

Fazit

Wieder konnte eine Anforderung schnell und einfach mit Ansible umgesetzt werden. Ob dies bereits der Königsweg ist bzw. ob dieser überhaupt existiert, steht jedoch noch nicht fest. Anstatt die Dateien zuerst auf das Ziel zu kopieren und dort Cron-Einträge für die Ausführung zu erstellen, könnte auch das Modul script[4. Ansible: script – Runs a local script on a remote node after transferring it] genutzt werden. Dieses Modul transferiert ein lokales Skript zum Zielsystem und führt es dort aus.

Bisher habe ich mich gegen den Einsatz des script-Moduls entschieden, da mir noch nicht klar ist, ob das Skript auf dem Zielsystem verbleibt oder bei jeder Ausführung erneut übertragen wird. Letzteres wäre bei einer großen Anzahl Zielsysteme nicht unbedingt wünschenswert.

Ansible – Das Modul yum_repository

Das Ansible Modul yum_repositoryist dazu geeignet, Repositories zu RPM-basierten Linux-Distributionen hinzuzufügen bzw. zu entfernen. Es findet z.B. in unserer Umgebung Anwendung, um Repositories von unserem lokalen Spiegelserver anzubinden. Der folgende Text gibt ein Beispiel zur Anwendung dieses Ansible-Moduls.

Auf meiner Spielwiese verwende ich folgende Konfiguration, um zwei Repositories hinzuzufügen.

Hinweis: Die folgende Konfiguration ist nicht zur Nachahmung empfohlen. Es handelt sich dabei um meine ersten Schritte mit Ansible. Es ist daher wahrscheinlich, dass die Konfiguration noch Fehler enthält, nicht optimiert ist und ein großes Verbesserungspotenzial besitzt. Die Nachahmung erfolgt ausdrücklich auf eigene Gefahr. Über Hinweise, wie man es besser machen kann, freue ich mich jederzeit.

Verzeichnisstruktur

Abgeleitet aus den „Best Practices“ von Ansible, verwende ich auf meiner Spielwiese folgende Verzeichnisstruktur:

ansible/
|-- group_vars
|   `-- e-stage               # variables for group e-stage
|-- hosts                     # inventory file
|-- host_vars                 # for system specific variables
|-- roles
|   |-- common                # this hierarchy represents a "role"
|   |   |-- defaults          
|   |   |-- files
|   |   |-- handlers
|   |   |   `-- main.yml      #  handlers file
|   |   |-- meta
|   |   |-- tasks
|   |   |   `-- main.yml      #  tasks file can include smaller files if wanted
|   |   `-- vars
|   `-- std-repos
|       |-- defaults
|       |-- files
|       |-- handlers
|       |-- meta
|       |-- tasks
|       |-- templates
|       `-- vars
|-- set_repos.yml             # playbook which sets the repos
|-- site.yml                  # master playbook file
`-- staging                   # inventory file for staging environment

Das Playbook

Das Playbook ist sehr kurz gehalten und gibt im Wesentlichen nur an, welche Hosts auf welche Rolle gemapped werden sollen.

# set_repos.yml
---
- hosts: all
  roles:
    - std-repos

Das obige Playbook definiert, dass die Tasks aus der Datei roles/e-stage/tasks/main.yml ausgeführt werden sollen, welche wie folgt aufgebaut ist:

---
- name: Add rhel-e-stage-repository
  yum_repository:
    name: "{{ repo_name1 }}"
    description: "{{ repo_description1 }}"
    baseurl: "{{ repo_baseurl1 }}"
    gpgcheck: yes
    gpgkey: file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release

- name: Add my-own-repository
  yum_repository:
    name: "{{ repo_name2 }}"
    description: "{{ repo_description2 }}"
    baseurl: "{{ rebo_baseurl2 }}"
    gpgcheck: no

GroupVars

Nun existieren im Staging-Bereich häufig noch weitere Stages. So gibt es oftmals eine T-, E- und I-Stage. Dabei ist jeder Stage ein eigenes Stage-Repository zugeordnet. Um die Systeme in den unterschiedlichen Stages mit den dazugehörigen Stage-Repositories zu versorgen, werden diese mit Hilfe der group_vars den entsprechenden Gruppen zugewiesen.

Für die Server der Gruppe e-stage wird in der Datei group_vars/e-stage definiert:

# file: group_vars/e-stage
repo_name1: rhel-e-stage
repo_description1: Local RHEL-Repo for E-Stage
repo_baseurl1: http://repo.example.com/rhel-e-stage/

repo_name2: own-e-stage
repo_description2: Local OWN-Repo for E-Stage
repo_baseurl2: http://repo.example.com/own-e-stage/

Nach diesem Beispiel können nun weitere Variablen unter group_vars für andere Stages erstellt werden. Den Hosts in den entsprechenden Hostgruppen, werden auf diesem Wege die passenden Repositories hinzugefügt.

Linux-Benutzerkonten mit Ansible verwalten

Wie bereits in diesem Beitrag erwähnt, beschäftige ich mich aktuell mit der Evaluierung von Ansible.

In diesem Artikel dokumentiere ich ein Beispiel, wie das Modul user zur Administration von Linux-Benutzerkonten verwendet werden kann. Der Link zur offiziellen Dokumentation des user-Moduls befindet sich am Ende dieses Artikels.[1. Ansible Module: user – Manage user accounts {en}]

Hinweis: Die folgende Konfiguration ist nicht zur Nachahmung empfohlen. Es handelt sich dabei um meine ersten Schritte mit Ansible. Es ist daher wahrscheinlich, dass die Konfiguration noch Fehler enthält, nicht optimiert ist und ein großes Verbesserungspotenzial besitzt. Die Nachahmung erfolgt ausdrücklich auf eigene Gefahr. Über Hinweise, wie man es besser machen kann, freue ich mich jederzeit.

Anforderungen

  • Es soll ein lokales Benutzerkonto angelegt werden
  • Der Benutzer soll Mitglied der Gruppe „wheel“ werden
  • Es soll ein initiales Passwort für das Benutzerkonto gesetzt werden
  • Ein öffentlicher SSH-Schlüssel soll in die Datei authorized_keys des zu erstellenden Benutzers eingetragen werden
  • Konfiguration des SSH-Daemons

Vorbereitung

Abgeleitet aus den „Best Practices“[2. Ansible Best Practices – Directory Layout] verwende ich auf meiner Spielwiese folgende Verzeichnisstruktur:

ansible/
|-- autoupdate.txt            # ignore
|-- autoupdate.yml            # ignore
|-- check_reboot.             # playbook which sets the repos
|-- group_vars
|   `-- e-stage               # variables for group e-stage
|-- hosts                     # inventory file
|-- host_vars                 # for system specific variables
|-- roles
|   |-- common                # this hierarchy represents a "role"
|   |   |-- defaults          
|   |   |-- files
|   |   |-- handlers
|   |   |   `-- main.yml      # handlers file
|   |   |-- meta
|   |   |-- tasks
|   |   |   `-- main.yml      # tasks file can include smaller files if warranted
|   |   `-- vars
|   `-- e-stage
|       |-- defaults
|       |-- files
|       |-- handlers
|       |-- meta
|       |-- templates
|       `-- vars
|-- create_johnd.yml          # playbook which creates user johnd
|-- site.yml                  # master playbook file
`-- staging                   # inventory file for staging environment

17 directories, 11 files

Um das Passwort für den neuen Benutzer erstellen zu können, muss zuerst der passende Passwort-Hash generiert werden[3. How do I generate crypted passwords for the user module?]:

python -c "from passlib.hash import sha512_crypt; import getpass; print sha512_crypt.encrypt(getpass.getpass())"

Der generierte Hash wird auf der Standardausgabe ausgegeben. Dieser wird für das Playbook benötigt.

Playbook und Tasks

Das Playbook ist sehr kurz gehalten und gibt im Wesentlichen nur an, welche Hosts auf welche Rolle gemapped werden sollen.

# create_johnd.yml
---
- hosts: all
  roles:
    - common

Bei Verwendung dieses Playbooks werden die Tasks aus der Datei roles/common/tasks/main.yml ausgeführt, welche wie folgt aufgebaut wurde:

---
# Configure sshd_config on target system
- name: Enable Public-Key-Authentication
  lineinfile:
    dest: /etc/ssh/sshd_config
    regexp: "^PubkeyAuthentication"
    line: "PubkeyAuthentication yes"
    state: present
  notify:
    - reload sshd

- name: Set AuthorizedKeyFile
  lineinfile:
    dest: /etc/ssh/sshd_config
    regexp: "^AuthorizedKeysFile"
    line: "AuthorizedKeysFile      .ssh/authorized_keys"
    state: present
  notify:
    - reload sshd

- name: Disable PasswordAuthentication
  lineinfile:
    dest: /etc/ssh/sshd_config
    regexp: "^PasswordAuthentication"
    line: "PasswordAuthentication no"
    state: present
  notify:
    - reload sshd

# Add user johnd with specific uid in group 'wheel'
- user: name=johnd comment="John Doe" uid=100 groups=wheel password="PASSWORDHASH" shell=/bin/bash append=yes state=present
  notify:
    - deploy ssh-pub-keys

Die hier spezifizierten Tasks setzen die folgenden Aufgaben um:

  • Aktivierung der Public-Key-Authentication
  • Spezifikation des Standard-AuthorizedKeysFile
  • Deaktivierung der Password-Authentication

Was in dieser Datei noch nicht zu sehen ist, ist die Verteilung eines öffentlichen SSH-Schlüssels. Dies wird von dem Handler[4. Ansible Handlers – Running Operations On Change] deploy ssh-pub-keys übernommen.

Der Handler ist in der Datei roles/common/handlers/main.yml definiert. Es handelt sich dabei um einen Task, welcher nur ausgeführt wird, wenn der Benutzer erfolgreich erstellt wurde.

Fazit

Soweit zu meinen ersten Gehversuchen mit Ansible Playbooks. Meine Anforderungen können mit dem hier dokumentierten Playbook erfüllt werden.

In dem hier beschriebenen Beispiel habe ich angenommen, dass es sich bei dem zu erstellenden Benutzer um das erste lokale Benutzerkonto neben root handelt. Dieses soll über die Berechtigung verfügen, Kommandos mit sudo auszuführen und primär für die Arbeit auf dem System verwendet werden.

Die Verteilung des SSH-Schlüssels über einen Handler anzustoßen erscheint mir jedoch nicht optimal. Der Handler wird nur dann ausgeführt, wenn durch das user-Modul Änderungen am Zielsystem vorgenommen wurden. Weitere SSH-Schlüssel bzw. Änderungen an bereits vorhandenen SSH-Schlüsseln lassen sich auf diesem Weg nicht vornehmen.

Daher erscheint es mir sinnvoll, die Verteilung von SSH-Schlüsseln über das Modul authorized_key[5. Ansible Module: authorized_key – Adds or removes an SSH authorized key] in ein eigenes Playbook auszulagern.

Ansible – IT-Automation für Jedermann

Ansible[1. Ansible Homepage {en}] ist eine Open-Source-Plattform zur Orchestrierung und allgemeinen Konfiguration und Administration von Computern.[2. Ansible – Wikipedia] Ansible soll dabei helfen, Konfigurationsprozesse zu automatisieren und die Administration multipler Systeme zu vereinfachen. Damit verfolgt Ansible im Wesentlichen die gleichen Ziele wie z.B. Puppet[3. Puppet – Wikipedia], Chef[4. Chef – Wikipedia {en}] und Salt[5. Salt – Wikipedia {en}].

Ansible hat gegenüber den genannten Systemen das Alleinstellungsmerkmal, dass auf den verwalteten Rechnern kein Agent installiert werden muss. Der Zugriff erfolgt ausschließlich über das SSH-Protokoll. Dies ist ein Vorteil, da bei der Einführung von Ansible SSH-Key-Authentifizierungsverfahren genutzt werden können, die häufig schon existieren. Ein weiterer Vorteil ergibt sich in dem Fall, wenn man Ansible nicht mehr nutzen möchte. Es bleiben keine Softwarekomponenten auf den verwalteten Rechnern zurück, die wieder deinstalliert werden müssen.

Mir gefällt auf den ersten Blick, dass mir die Lösung zwei Wege bietet, um meine Systeme zu konfigurieren. Zum einen kann ich Playbooks[6. Ansible – Playbooks {en}] nutzen, welche gewissermaßen das Pendant der Puppet Manifeste darstellen. Zum anderen existiert ein Ad-hoc-Modus, welcher es erlaubt, die Konfiguration von Zielsystemen bei Bedarf anzupassen, ohne zuvor ein Rezept bzw. Playbook erstellt zu haben.

Die Playbooks selbst werden in YAML[7. YAML – Wikipedia]-Syntax verfasst. Dadurch wird eine gute Lesbarkeit sichergestellt. Zudem ist die Syntax recht einfach gehalten und daher schnell zu erlernen.

Für häufige Anwendungsfälle wie z.B. das Anlegen eines Benutzers, die Verteilung von SSH-Keys, die Installation verfügbarer Updates/Patches und noch vieles mehr, bringt Ansible vorgefertigte Module mit. Diese können direkt auf den Zielsystemen oder durch die Verwendung von Playbooks ausgeführt werden.[8. About Modules – Ansible {en}]

Die oben genannten Aspekte motivieren mich, zu evaluieren, ob Ansible für den Einsatz in meinem beruflichen Umfeld geeignet ist. In diesem Blog werden in unregelmäßigen Abständen weitere Artikel erscheinen, in denen ich Erkenntnisse aus meiner Evaluierung festhalte und einige kurze Beispiele zur Nutzung konkreter Module wiedergebe.

Wer dieser Reihe folgt und sich gern mit seinen Erfahrungen einbringen möchte, darf dies gerne tun. Ich freue mich jederzeit über Kommentare, Ideen und Anregungen.

Update vom 17.12.2016: Ich habe die Ansible-Artikel in diesem Blog in einer PDF-Datei zusammengefasst. Die Datei gibt es hier: Ansible-Kochbuch (PDF)