Ich bin immer auf der Suche nach cleveren Dingen, die ich mit Ansible machen kann. Bei so vielen Tools und Diensten, die HTTP-basierte Anwendungsprogrammierschnittstellen (APIs) nutzen, ist klar, dass die programmgesteuerte Interaktion mit API-gesteuerten Diensten von Ansible eine wertvolle Fähigkeit ist. Das mag nach einer erweiterten Funktion klingen, aber dieser Artikel führt Sie durch einen Anwendungsfall, der zeigt, wie selbst einfache Umgebungen von der Leistungsfähigkeit und Einfachheit des URI-Moduls von Ansible profitieren können.
Interaktion mit einfachen Endpunkten
Zuerst führe ich Sie durch ein Playbook, das die HTTP-Fähigkeiten von Ansible nutzt, um während eines Webserver-Upgrades intelligente Entscheidungen zu treffen. Das Playbook unten:
- Führt ein Wartungsskript aus.
- Überprüft, ob ein Health Check API-Endpunkt einen HTTP-503-Dienst vorübergehend nicht verfügbar zurückgibt Nachricht.
- Führt ein Skript aus, um die Anwendung zu aktualisieren.
- Führt ein Skript nach der Wartung aus, um den Webserver anzuweisen, wieder normal zu reagieren.
- Überprüft die Zustandsprüfungs-API erneut, um sicherzustellen, dass sie mit 200 OK antwortet .
Hier ist das Playbook:
---
- hosts: all
tasks:
- name: Run maintenance start script
command:
cmd: /usr/local/sbin/start_maintenance.sh
- name: Confirm that 503 Unavailable response is returned
uri:
url: "http://{{ ansible_host }}/api/v1/healthcheck"
status_code: 503
- name: Update application
command:
cmd: /usr/local/sbin/update_app.sh
- name: Run maintenance end script
command:
cmd: /usr/local/sbin/end_maintenance.sh
- name: Confirm that 200 OK response is returned
uri:
url: "http://{{ ansible_host }}/api/v1/healthcheck"
status_code: 200
Ich verwende das URI-Modul von Ansible, um /api/v1/healthcheck
zu erreichen auf dem Server. Der erste URI-Aufruf erwartet ein HTTP 503 Statuscode, der zurückgegeben werden soll, da sich der Server im Wartungsmodus befinden sollte und keine Anforderungen bearbeitet. Nach dem Upgrade erwartet der URI-Aufruf ein HTTP 200 Statuscode, der anzeigt, dass der Webserver wieder fehlerfrei ist.
Dieser einfache Ansatz verbessert die Sicherheit meines Playbooks. Wenn der Server nicht in den Wartungsmodus wechselt, führt Ansible keine Patches durch:
fsh$ ansible-playbook -i inventory.ini playbook-healthcheck.yml
PLAY [all] ***********************************************************************************
TASK [Gathering Facts] ***********************************************************************
ok: [nyc1-apiserver-1.example.com]
TASK [Run maintenance start script] **********************************************************
changed: [nyc1-apiserver-1.example.com]
TASK [Confirm that 503 Unavailable response is returned] *************************************
fatal: [nyc1-apiserver-1.example.com]: FAILED! => changed=false
connection: close
content: ''
content_length: '0'
content_type: application/octet-stream
cookies: {}
cookies_string: ''
date: Fri, 11 Sep 2020 18:35:08 GMT
elapsed: 0
msg: 'Status code was 200 and not [503]: OK (0 bytes)'
redirected: false
server: nginx
status: 200
url: http://nyc1-apiserver-1.example.com/api/v1/healthcheck
PLAY RECAP ***********************************************************************************
nyc1-apiserver-1.example.com : ok=2 changed=1 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0
Wenn der Server nach dem Patchen nicht korrekt wieder hochfährt, schlägt Ansible mit folgendem Fehler fehl:
fsh$ ansible-playbook -i inventory.ini playbook-healthcheck.yml
PLAY [all] ***********************************************************************************
TASK [Gathering Facts] ***********************************************************************
ok: [nyc1-apiserver-1.example.com]
TASK [Run maintenance start script] **********************************************************
changed: [nyc1-apiserver-1.example.com]
TASK [Confirm that 503 Unavailable response is returned] *************************************
ok: [nyc1-apiserver-1.example.com]
TASK [Update application] ********************************************************************
changed: [nyc1-apiserver-1.example.com]
TASK [Run maintenance end script] ************************************************************
changed: [nyc1-apiserver-1.example.com]
TASK [Confirm that 200 OK response is returned] **********************************************
fatal: [nyc1-apiserver-1.example.com]: FAILED! => changed=false
connection: close
content: |-
<html>
<head><title>503 Service Temporarily Unavailable</title></head>
<body>
<center><h1>503 Service Temporarily Unavailable</h1></center>
<hr><center>nginx</center>
</body>
</html>
content_length: '190'
content_type: text/html; charset=utf-8
date: Fri, 11 Sep 2020 18:55:01 GMT
elapsed: 0
msg: 'Status code was 503 and not [200]: HTTP Error 503: Service Temporarily Unavailable'
redirected: false
server: nginx
status: 503
url: http://nyc1-apiserver-1.example.com/api/v1/healthcheck
PLAY RECAP ***********************************************************************************
nyc1-apiserver-1.example.com : ok=5 changed=3 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0
Dies sind einfache Überprüfungen, die in fast jedes Handbuch eingebaut werden können, um bessere Sicherheitsgarantien hinzuzufügen, bevor Unterbrechungsarbeiten durchgeführt werden, oder um sicherzustellen, dass Unterbrechungsarbeiten erfolgreich waren, bevor sie als Erfolg bezeichnet werden.
Parsing hat JSON zurückgegeben
Das vorherige Beispiel eignet sich hervorragend für einfache, auf dem HTTP-Status basierende Zustandsprüfungen. In der Regel möchten Sie jedoch einige Daten von einem Webendpunkt abrufen und dann etwas mit den zurückgegebenen Daten tun. Zum Beispiel:Was ist, wenn ich die Anwendungsversion über einen exponierten Endpunkt überprüfen und nur Updates durchführen möchte, wenn sie nicht auf dem neuesten Stand sind?
Meine Demoanwendung hat genau einen solchen Endpunkt. Bei einer Abfrage wird die aktuelle Version der Anwendung zurückgegeben:
fsh$ http nyc1-apiserver-1.example.com/api/v1/appVersion
HTTP/1.1 200 OK
Accept-Ranges: bytes
Connection: keep-alive
Content-Length: 24
Content-Type: application/json
Date: Fri, 11 Sep 2020 18:36:15 GMT
ETag: "5f5bc33b-18"
Last-Modified: Fri, 11 Sep 2020 18:34:35 GMT
Server: nginx
{
"appVersion": "1.0.1"
}
Hinweis :Neugierig auf den HTTP-Befehl, den ich ausgeführt habe? Sehen Sie sich den Artikel meines Sudo-Kollegen Jonathan Roemer über HTTPie an.
Ich kann das zurückgegebene JSON von diesem Endpunkt verwenden, um Entscheidungen in meinem Ansible-Playbook zu treffen. Die vorherige Version dieses Playbooks führte immer das Anwendungsaktualisierungsskript aus. Ich kann dies jedoch verbessern, indem ich die Anwendung nur dann aktualisiere, wenn sie meine gewünschten Versionsanforderungen nicht erfüllt:
---
- hosts: all
vars:
desired_app_version: "1.0.1"
tasks:
- name: Check API version
uri:
url: "http://{{ ansible_host }}/api/v1/appVersion"
register: api_version_result
- name: Perform maintenance tasks
block:
- name: Run maintenance start script
command:
cmd: /usr/local/sbin/start_maintenance.sh
- name: Confirm that 503 Unavailable response is returned
uri:
url: "http://{{ ansible_host }}/api/v1/healthcheck"
status_code: 503
- name: Update application
command:
cmd: /usr/local/sbin/update_app.sh
- name: Run maintenance end script
command:
cmd: /usr/local/sbin/end_maintenance.sh
- name: Confirm that 200 OK response is returned
uri:
url: "http://{{ ansible_host }}/api/v1/healthcheck"
status_code: 200
- name: Check API version after updates
uri:
url: "http://{{ ansible_host }}/api/v1/appVersion"
register: updated_api_version_result
failed_when: updated_api_version_result['json']['appVersion'] != desired_app_version
when: api_version_result['json']['appVersion'] != desired_app_version
Dieses Playbook stellt einige nützliche Ansible-Konzepte vor. Zunächst können Sie sehen, dass das URI-Modul die /api/v1/appVersion
erreicht API-Endpunkt und registriert die Ausgabe dieses URI-Aufrufs in einer Variablen. Die Update-Tasks wurden in einen Block verschoben, was eine logische Gruppierung von Tasks ermöglicht. Die Hinzufügung des when -Klausel bewirkt, dass dieser Block nur ausgeführt wird, wenn sich die aktuelle App-Version von der gewünschten App-Version unterscheidet, wie von /api/v1/appVersion
zurückgegeben Endpunkt. Schließlich habe ich dem Update-Prozess eine zusätzliche Überprüfung hinzugefügt. Nachdem die Updates ausgeführt wurden, erfolgt ein weiterer Aufruf der /api/v1/appVersion
endpoint stellt sicher, dass das Update erfolgreich war und die aktuelle App-Version mit der gewünschten Version übereinstimmt. Dabei wird die failed_when-Syntax verwendet, mit der Sie bestimmte Fehlerkriterien für Aufgaben definieren können.
Im Klartext ausgedrückt besagt diese Ansible-Blocklogik:„Führen Sie die Anwendungswartungs- und Installationsskripte nur aus, wenn die aktuelle Version der App nicht der gewünschten Version der App entspricht. Stellen Sie nach Abschluss der Aktualisierung sicher, dass die App wirklich aktualisiert wurde.“
Mit nur wenigen Zeilen Ansible-Code habe ich eine leistungsstarke, aber einfache Möglichkeit geschrieben, JSON zu verwenden, das von einem API-Endpunkt zurückgegeben wird, um intelligente Entscheidungen in meinen Playbooks zu treffen.
Interaktion mit einem authentifizierten Endpunkt
Bisher habe ich die Interaktion mit API-Endpunkten behandelt, die keine Authentifizierung erfordern. Sie sind jedoch wahrscheinlich am ehesten an die Interaktion mit APIs gewöhnt, die eine Art von Authentifizierung erfordern, z. B. ein API-Token. Das URI-Modul unterstützt dies, indem es die Header und den Body einer HTTP-Anforderung festlegt.
[Das könnte Ihnen auch gefallen:9 Ansible-Leitfäden, die Ihnen den Einstieg in die Automatisierung erleichtern]
Ich kann mein Wartungs-Playbook noch einen Schritt weiter bringen, indem ich die Alarmierung auf jedem Host in meinem Überwachungssystem deaktiviere und wieder aktiviere. Dazu muss ein POST gesendet werden Anfrage an einen API-Endpunkt auf dem Überwachungsserver. Die Anfrage muss mein API-Token und den Host im JSON-codierten Text enthalten. Ansible macht dies einfach. Hier ist das endgültige Playbook:
---
- hosts: all
vars:
desired_app_version: "1.0.1"
api_token: "8897e9a6-b10c-42c8-83a2-c83e9c8b6703"
tasks:
- name: Check API version
uri:
url: "http://{{ ansible_host }}/api/v1/appVersion"
register: api_version_result
- name: Perform maintenance tasks
block:
- name: Disable host in monitoring
uri:
url: "http://nyc1-monitoring-1.example.com/api/v1/startMaintenance"
method: POST
headers:
X-API-KEY: "{{ api_token }}"
body_format: json
body:
host: "{{ ansible_host }}"
- name: Run maintenance start script
command:
cmd: /usr/local/sbin/start_maintenance.sh
- name: Confirm that 503 Unavailable response is returned
uri:
url: "http://{{ ansible_host }}/api/v1/healthcheck"
status_code: 503
- name: Update application
command:
cmd: /usr/local/sbin/update_app.sh
- name: Run maintenance end script
command:
cmd: /usr/local/sbin/end_maintenance.sh
- name: Confirm that 200 OK response is returned
uri:
url: "http://{{ ansible_host }}/api/v1/healthcheck"
status_code: 200
- name: Check API version after updates
uri:
url: "http://{{ ansible_host }}/api/v1/appVersion"
register: updated_api_version_result
failed_when: updated_api_version_result['json']['appVersion'] != desired_app_version
- name: Re-enable host in monitoring
uri:
url: "http://nyc1-monitoring-1.example.com/api/v1/stopMaintenance"
method: POST
headers:
X-API-KEY: "{{ api_token }}"
body_format: json
body:
host: "{{ ansible_host }}"
when: api_version_result['json']['appVersion'] != desired_app_version
Ich verwende jetzt das URI-Modul, um HTTP POST zu senden Anfragen (anstelle des standardmäßigen GET Anfragen) an /api/v1/startMaintenance
und /api/v1/stopMaintenance
Endpunkte auf nyc1-monitoring-1.example.com . Diese Anforderungen enthalten mein API-Token für den Überwachungsserver im Header und den Hostnamen des Servers im Text. Wenn eine der Anfragen mit einem Wert ungleich 200 fehlschlägt Statuscode, dann schlägt das gesamte Ansible-Playbook fehl.
Hinweis :In der Praxis möchten Sie etwas wie Ansible Vault verwenden, um ein API-Token zu speichern, anstatt es direkt im Playbook zu platzieren.
Mit diesen letzten Aufgaben kann ich meinen Upgrade-Workflow vollständig automatisieren:Durchführen von Versionsprüfungen, Interagieren mit externer Überwachung zum Deaktivieren von Warnmeldungen für ein System und Sicherstellen, dass der Server vor und nach dem Patchen die richtigen HTTP-Statuscodes zurückgibt. Ich habe jetzt einen End-to-End-Workflow, der viele der üblichen Schritte automatisiert, denen ich folge, wenn ich Upgrades auf einem System durchführe.
[ Brauchen Sie mehr zu Ansible? Nehmen Sie an einem kostenlosen technischen Überblickskurs von Red Hat teil. Ansible Essentials:Einfachheit in der Automatisierung Technischer Überblick. ]
Abschluss
Dieser Artikel begann mit einem einfachen Playbook, das grundlegende Webprüfungen gegen nicht authentifizierte API-Endpunkte durchführte. Ich habe Sie durch das Parsen von JSON-Antworten und sogar durch die Interaktion mit authentifizierten API-Endpunkten geführt, indem ich benutzerdefinierte Header und Body-Inhalte für HTTP-Anforderungen festgelegt habe. Ansible macht die Interaktion mit Webdiensten einfach, und ich bin zuversichtlich, dass Sie selbst in einfachen Umgebungen Verwendung für diese Art von Ansatz finden werden.
Wenn Sie mehr lernen und sich selbst herausfordern möchten, finden Sie hier einige Ideen, die Sie selbst umsetzen können:
- Verwenden Sie das URI-Modul, um mit Ihrem bevorzugten API-basierten Webdienst zu interagieren.
- Sieh nach, ob du herausfinden kannst, wie man sich mit Client-Zertifikaten authentifiziert.
- Erfahren Sie, wie Sie mit Ansible ein Formular an eine Website übermitteln.