GNU/Linux >> LINUX-Kenntnisse >  >> Linux

Verwenden von Ansible zur Interaktion mit Web-Endpunkten

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:

  1. Führt ein Wartungsskript aus.
  2. Überprüft, ob ein Health Check API-Endpunkt einen HTTP-503-Dienst vorübergehend nicht verfügbar zurückgibt Nachricht.
  3. Führt ein Skript aus, um die Anwendung zu aktualisieren.
  4. Führt ein Skript nach der Wartung aus, um den Webserver anzuweisen, wieder normal zu reagieren.
  5. Ü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.

Linux
  1. Virtuelle Multipass-Maschinen mithilfe von Ansible

  2. Automatisieren von Webanfragen mit Curl?

  3. Zeilenumbruch mit Mc verwenden?

  4. Dateiberechtigungen unter Linux mit Beispiel

  5. So führen Sie Packet Sniffing mit Libpcap mit C-Beispielcode durch

Spiegeln Sie Ihre Website mit rsync

Verwenden von Wget mit FTP zum rekursiven Herunterladen/Verschieben von Websites

So arbeiten Sie mit Ansible Provisioner in Vagrant

Überwachen Sie die Linux-Aufgabe mit SysMonTask

Starten Sie eine Windows-Webfarm mit Web Deploy

Farben mit printf verwenden