Ansible
- Nützliche Hinweise
- Strukturen, Playbooks, Vaults - so habe ich es gemacht
- Playbook - install Java 8 JDK
Nützliche Hinweise
Links zu Ansible Docs
Überprüfung der Erreichbarkeit aller Hosts
root@server:/etc/ansible# ansible -i /etc/ansible/INVENTORY -m ping HOST-GRUPPE
server-01 | SUCCESS => {
"changed": false,
"ping": "pong"
}
server-02 | SUCCESS => {
"changed": false,
"ping": "pong"
}
server-03 | SUCCESS => {
"changed": false,
"ping": "pong"
}
Verwendung der shell
Abfragen ob ein paket installiert ist:
root@server#ansible -i /etc/ansible/INVENTORY -m shell -a "dpkg -l | grep PAKETNAME" HOST-GRUPPE
Hier mal ein Beispiel:
root@server:/etc/ansible# ansible -i /etc/ansible/INVENTORY -m shell -a "dpkg -l | grep lsb-re" linuxserver
server-01 | SUCCESS | rc=0 >>
ii lsb-release 9.20161125 all Linux Standard Base version report utility
server-02 | SUCCESS | rc=0 >>
ii lsb-release 9.20161125 all Linux Standard Base version reping utility
server-03 | SUCCESS | rc=0 >>
ii lsb-release 9.20161125 all Linux Standard Base version reporting uity
Arbeiten mit Variablen, geschützten sensiblen Daten in Variablen usw.
So könnt ihr Variablen in config Dateien aufrufen:
variableABC: "{{ variablexyz }}"
Ich nutze das z.B. um Passwörter zu schützen, die ich in einer sog. "Ansible Vault" verschlüsselt gespeichert habe.
Hier gehts zur Ansible Doku zum Thema Vault.
Variablen - Hostnamen aus dem Inventory in config files einbauen
Wenn man z.b. ein Cluster für Graylog hat und möchte diese Cluster Nodes mit einer einheitlichen Konfiguration für einen Telegraf Datensammler bestücken dann muss man nicht für jeden Node eine separate telegraf.conf bereitstellen.
Ich habe es so gelöst:
1. einheitliche telegraf.conf Datei:
[[inputs.graylog]]
metrics = ["...."]
password = "Graylog-Passwort"
servers = ["http://#graylog-node:9000/api/system/metrics/multiple"]
username = "graylog-user"
2. Ansible Playbook
- In der Hostgruppe "g-gl-cluster" befinden sich die Hosts, die zum Graylog gehören.
- Mit dem Aufruf "{{ inventory_hostname }}" wird dann der Hostname des Hosts, der gerade bearbeitet wird, aus dem Inventory ausgelesen und an die entsprechende Stelle in die config Datei. Dazu wird das Ansible Modul "lineinfile" benutzt: https://docs.ansible.com/ansible/latest/modules/lineinfile_module.html#lineinfile-module
---
- hosts: g-gl-cluster
tasks:
- name: copy the telegraf config file to the group g-prometheus-clients
copy:
src: "/etc/ansible/etc-configs/telegraf/telegraf-inputs-graylog.conf"
dest: "/etc/telegraf/telegraf.d/telegraf-inputs-graylog.conf"
owner: root
group: root
mode: 0644
backup: yes
- name: Replace hostname in telegraf config file
lineinfile:
path: '/etc/telegraf/telegraf.d/telegraf-inputs-graylog.conf'
regexp: '^servers'
line: 'servers = ["http://{{ inventory_hostname }}:9000/api/system/metrics/multiple"]'
state: present
create: yes
- name: Restart service telegraf, in all cases
service:
name: telegraf
state: restarted
Ausführung von Playbooks auf Hosts beschränken
#ansible-playbook dein-playbook.yml --limit=einzelner-host
Strukturen, Playbooks, Vaults - so habe ich es gemacht
Prolog
Ich möchte meine Erfahrungen mit Ansible mit euch teilen und werde hier mal aufführen wie ich Ansible auf meinem Lernweg aufbaue. Bedenkt aber stets, dass ich gerade erst mit Ansible anfange und vorher noch nie etwas mit zentralem Konfigurationsmanagement zu tun hatte. Ich zeige euch hier lediglich meine Überlegungen, die wahrscheinlich hier und da noch verbessert werden müssten. Ein Nachbauen macht ihr immer auf eigene Gefahr hin.
Vorraussetzungen - Ansible Eigenheiten
Was solltet ihr beachten bzw. vorausschauend planen, wenn ihr Ansible einsetzen wollt?
Benutzer für zentrale Verwaltungsaufgaben
Das Arbeiten mit Ansible wird euch viel einfacher fallen, wenn ihr einen einzigen Benutzer auf allen euren Hosts angelegt habt und dieser SUDO Rechte hat. Dann könnt ihr euer Ansible Management einheitlich aufbauen und habt nicht zig verschiedene User, die ihr berücksichtigen müsst.
Ja, das ist sicherlich Geschmackssache und in manchen Situationen im Bezug auf Sicherheit sollte man das abwandeln und auf mehrere spezifische Benutzer zurückgreifen.
Ordnerstruktur
Ansible Docs: https://docs.ansible.com/ansible/latest/user_guide/playbooks_best_practices.html#directory-layout
Von Ansible wird keine Struktur vorgegeben. Man hat also die freie Entscheidungsgewalt. So habe ich also das hier angefangen zu strukturieren:
root@server:/etc/ansible# tree
.
├── ansible.cfg
├── archive
│ ├── azure.bak
│ ├── configure-linuxserver-rsyslog-to-graylog.yml.alt
│ ├── hosts.bak
│ └── install-prometheus-node-exporter.yml.bak
├── azure
├── group_vars
│ ├── production
│ │ ├── vars
│ │ └── vault
│ ├── init-linuxserver
│ │ ├── vars
│ │ └── vault
│ └── linuxserver
│ ├── vars
│ └── vault
├── playbooks
│ ├── packages
│ │ ├── install-production-prometheus-node-exporter.yml
│ │ ├── install-prometheus-node-exporter.yml
│ │ └── install-systemtools.yml
│ ├── services
│ │ └── connect-graylog.yml
│ ├── systemsettings
│ │ ├── set-timezone-europe-berlin.yml
│ │ └── update-hosts-file.yml
│ └── users
│ └── create-user-webdata.yml
└── vault_pwd
Variablen
So sieht eine vars Datei für die Gruppe linuxserver aus:
root@server:/etc/ansible# cat group_vars/linuxserver/vars
---
ansible_connection: ssh
ansible_user: ansibleadmin
ansible_ssh_private_key_file: /home/ansibleadmin/.ssh/id_rsa
ansible_ssh_common_args: '-o StrictHostKeyChecking=no'
ansible_become: yes
ansible_become_method: sudo
ansible_become_user: root
ansible_become_pass: "{{ vault_ansible_become_pass }}"
Inventory
Ansible Docs: https://docs.ansible.com/ansible/latest/user_guide/intro_inventory.html
Meine Inventory Datei heißt azure, weil ich Ansible im Zuge einer Azure Evaluierung einsetze.
root@vm-azure-manager:/etc/ansible# cat azure
#Here i was experimenting where do set this option for being applied everywhere.
#ansible_ssh_common_args='-o StrictHostKeyChecking=no'
[prometheus]
prometheus-01
[grafana]
grafana-01
[graylog]
graylog-01
[es-nodes]
es-node-01
[init-linuxserver:children]
prometheus
grafana
graylog
es-nodes
[linuxserver:children]
prometheus
grafana
graylog
es-nodes
[graylogclients:children]
grafana
prometheus
es-nodes
Bisher habe ich folgendes gemeistert:
- Einstellungen in separate Unterverzeichnisse zu kapseln und Gruppen bezogen Einstellungen zu vergeben
- Hosts in Gruppen zu unterteilen
- Gruppen zu verschachteln
Durch die Verschachtelung von Gruppen kann ich meine Hosts kategorisieren und habe eine ähnliche Vorangehensweise wie beim Administrieren von Benutzerrechten, d.h. bei Änderungen den Aufwand zu minimieren, weil man z.B. einen neuen Host nur noch einer Gruppe zuweist oder einen alten aus einer Gruppe löschen muss.
Ansible Vault
Ansible Docs: https://docs.ansible.com/ansible/latest/user_guide/vault.html
Ich habe am Anfang die Passwörter für meine User, die Ansible benutzen sollte, klar lesbar in meine Konfig-Dateien geschrieben. Da ich davon aber absolut kein Freund bin, habe ich mich also zeitnah darum gekümmert und herausgefunden, dass es die sog. Ansible-Vault gibt. Vault heißt Tresor. Also ein verschlüsselter Container, der eure Passwörter und sonstige Einstellungen beinhalten kann. Ich habe aktuell nur ein Passwort darin gespeichert.
Wie ihr schon anhand meiner Ordnerstruktur gesehen habt, gibt es jeweils in jedem Unterordner einer Gruppe eine Datei namens "vault". Das sind meine geschützten Bereiche für die jeweilige Gruppe.
So hier habe ich es gemacht:
- Eine Schlüsseldatei "vault_pwd" erstellt und in Klartext den Schlüssel eingetragen, der global zum Ver- und Entschlüsseln benutzt werden soll.
Man muss keinen globalen Schlüssel verwenden. Das kann man so einstellen wie man es möchte.
root@server:# nano vault_pwd
- Schlüssel eintragen
- Globale Schlüsseldatei in der ansbile.cfg aktiviert
vault_password_file = /etc/ansible/vault_pwd
- zu verschlüsselnde Variablen mit einem Editor in die jeweilige vault Datei eintragen
- vault Datei verschlüsseln:
ansible-vault encrypt vault
-
Da wir einen globalen Schlüssel aktiviert haben, wird dieser automatisch zum Verschlüsseln benutzt, sodass ihr keinen Schlüssel an der Stelle eingeben müsst.
- So habe ich z.B. das Passwort hinterlegt, dass benutzt wird um SUDO Rechte zu aktivieren:
-
vault_ansible_become_pass: DAS-PASSWORT
- Ansible empfiehlt als Variablennamen der zu verschlüsselnden Variable ein "vault_" als Präfix zu verwenden. Ich finde das gut, da man so gleich den Bezug der Verwendung herstellen kann.
-
-
- Passwort in unverschlüsselter Variable aus verschlüsselter Variable aufrufen
- So sieht der Aufruf dann in meiner "vars" Datei aus:
ansible_become_pass: "{{ vault_ansible_become_pass }}"
- Die Syntax nennt sich jinja2
- So sieht der Aufruf dann in meiner "vars" Datei aus:
Alles was ich jetzt mit Ansible mache, funktioniert mit verschlüsselten Passwörtern.
Playbooks
Ansible Docs: https://docs.ansible.com/ansible/latest/user_guide/playbooks.html
Ansible-Systembenutzer bereitstellen
Funktionen
- User mit Home Verzeichnis anlegen
- SSH Key hinterlegen
- SUDO Rechte vergeben
Probleme
Man benötigt natürlich auf allen Hosts für das erstmalige Bereitstellen mit Ansible einen Systemuser mit SUDO Rechten. Da ich für Ansible Arbeiten einen eigenen Systemuser nutzen möchte, habe ich als erstes einen neuen user auf allen Hosts angelegt. Dafür habe ich den User benutzt, den man mit der Installation von Debian anlegen muss.
Playbook "create-user-ansibleadmin.yml
root@server:/etc/ansible# cat playbooks/users/create-user-ansibleadmin.yml
---
- hosts: init-linuxserver
remote_user: initial-user-from-fresh-install-with-sudo-rights
become: yes
become_method: sudo
become_user: root
tasks:
- name: Ansible create user ansibleadmin with home directory
user:
name: ansibleadmin
createhome: yes
- name: create directory .ssh
file:
path: /home/ansibleadmin/.ssh
state: directory
owner: ansibleadmin
group: ansibleadmin
mode: 0700
- name: Ansible copy the ssh public key of the user ansibleadmin to the remote host
copy:
src: "/home/ansibleadmin/.ssh/id_rsa.pub"
dest: "/home/ansibleadmin/.ssh/authorized_keys"
owner: ansibleadmin
group: ansibleadmin
- name: Add user ansibleadmin to sudo group and grant sudo rights
user:
name: ansibleadmin
groups: sudo
Debian Paket mit apt installieren
Playbook: "install-systemtools.yml"
root@server:/etc/ansible# cat playbooks/packages/install-systemtools.yml
---
- hosts: linuxserver
tasks:
- name: install lsb-release
apt: name=lsb-release state=present
- name: install lsof
apt: name=lsof state=present
Playbook: Azure CLI installieren
---
- hosts: azure-master
tasks:
- name: Install apt-transport-https
apt: name=apt-transport-https state=present
- name: Add Azure Repository to sources.list.d
apt_repository:
repo: deb [arch=amd64] https://packages.microsoft.com/repos/azure-cli/ stretch main
state: present
filename: azure-cli
- name: Add Microsoft´s signature key
apt_key:
url: https://packages.microsoft.com/keys/microsoft.asc
state: present
- name: make an apt-update before be able to find azure-cli package
apt:
update_cache: yes
- name: Install Azure CLI
apt: name=azure-cli state=present
Playbook: Docker CE installieren
---
- hosts: docker-vm
tasks:
- name: Add Azure Repository to sources.list.d
apt_repository:
repo: deb [arch=amd64] https://download.docker.com/linux/debian stretch stable
state: present
filename: docker-ce
- name: Add Docker CE signature key
apt_key:
url: https://download.docker.com/linux/debian/gpg
state: present
- name: make an apt-update before be able to find azure-cli package
apt:
update_cache: yes
- name: Install Docker CE
apt: name=docker-ce state=present
Datei kopieren - hosts Datei bereitstellen
Playbook "update-hosts-file.yml"
root@server:/etc/ansible# cat playbooks/systemsettings/update-hosts-file.yml
---
- hosts: linuxserver
# steht alles in den "Vars" Dateien
# remote_user: ansibleadmin
# become: yes
# become_method: sudo
# become_user: root
tasks:
- name: copy the hosts file from host "server" to the group linuxserver
copy:
src: "/etc/hosts"
dest: "/etc/hosts"
owner: root
group: root
Hintergrund
Ich arbeite in meiner testumgebung mit den Hosts Dateien, weil ich auf die Schnelle kein DNS aufbauen konnte. Zum Einen bin ich kein Experte für DNS (erst einmal damit rumprobiert) und Zweitens wollte ich so schnell wie möglich alle Hosts mit Namen anstelle von IP Adressen ansprechen.
Zeitzone einstellen
Playbook "set-timezone-europe-berlin.yml"
root@server:/etc/ansible# cat playbooks/systemsettings/set-timezone-europe-berlin.yml
---
- hosts: linuxserver
tasks:
- name: set timezone to Europe/Berlin
timezone:
name: Europe/Berlin
Hintergrund
Da ich die Debian VMs in Azure über den RessourcenManager von Azure bereitstellen lassen habe, gab es nachträglich Einstellungen, die angepasst werden mussten. Dazu gehörte u.a. die Zeitzone.
Playbook - install Java 8 JDK
# cat /etc/ansible/playbooks/apt/i-java8jdk.yml ---
- hosts: g-java8jdk
tasks:
- name: Add Java 8 JDK Repository to sources.list.d
apt_repository:
repo: deb http://ppa.launchpad.net/webupd8team/java/ubuntu xenial main
state: present
filename: webupd8team-java8jdk
- name: Add webupd8team signature key
apt_key:
keyserver: keyserver.ubuntu.com
id: EEA14886
- name: make an apt-update before be able to find java packages
apt:
update_cache: yes
- name: set licence selected
shell: /bin/echo debconf shared/accepted-oracle-license-v1-1 select true | /usr/bin/debconf-set-selections
sudo: yes
- name: set licence seen
shell: /bin/echo debconf shared/accepted-oracle-license-v1-1 seen true | /usr/bin/debconf-set-selections
sudo: yes
- name: Install oracle-java8-installer
apt: name={{item}} state=present
with_items:
- java-common
- oracle-java8-installer
- oracle-java8-set-default