GNU/Linux >> Tutoriels Linux >  >> Linux

Utilisation d'Ansible pour interagir avec les points de terminaison Web

Je suis toujours à la recherche de choses intelligentes à faire avec Ansible. Avec autant d'outils et de services tirant parti des interfaces de programmation d'applications (API) basées sur HTTP, il est clair que l'interaction avec les services pilotés par API d'Ansible par programmation est une capacité précieuse. Cela peut sembler être une fonctionnalité avancée, mais cet article vous présente un cas d'utilisation qui montre comment même des environnements simples peuvent bénéficier de la puissance et de la simplicité du module URI d'Ansible.

Interagir avec des terminaux simples

Tout d'abord, je vais vous guider à travers un playbook qui exploite les capacités HTTP d'Ansible pour prendre des décisions intelligentes lors d'une mise à niveau d'un serveur Web. Le playbook ci-dessous :

  1. Exécute un script de maintenance.
  2. Vérifie pour s'assurer qu'un point de terminaison de l'API de vérification de l'état renvoie un HTTP Service 503 temporairement indisponible message.
  3. Exécute un script pour mettre à niveau l'application.
  4. Exécute un script de post-maintenance pour indiquer au serveur Web de recommencer à répondre normalement.
  5. Revérifie l'API de vérification de l'état pour s'assurer qu'elle répond avec 200 OK .

Voici le 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

J'utilise le module URI d'Ansible pour atteindre /api/v1/healthcheck sur le serveur. Le premier appel URI attend un HTTP 503 code d'état à renvoyer car le serveur doit être en mode maintenance et ne pas traiter les demandes. Après la mise à niveau, l'appel URI attend un HTTP 200 code d'état, indiquant que le serveur Web est à nouveau sain.

Cette approche simple améliore la sécurité de mon playbook. Si le serveur ne parvient pas à entrer en mode maintenance, Ansible n'effectuera aucun correctif :

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  

Si le serveur ne parvient pas à se rétablir correctement après le correctif, Ansible échoue avec une erreur :

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   

Il s'agit de vérifications simples qui peuvent être intégrées à presque tous les manuels pour ajouter de meilleures garanties de sécurité avant d'effectuer un travail perturbateur ou pour s'assurer que le travail perturbateur a réussi avant de le qualifier de réussi.

L'analyse a renvoyé JSON

L'exemple précédent fonctionne très bien pour les vérifications d'état simples basées sur le statut HTTP. Cependant, vous souhaiterez généralement récupérer certaines données à partir d'un point de terminaison Web, puis faire quelque chose avec les données renvoyées. Par exemple :que se passe-t-il si je souhaite vérifier la version de l'application via un point de terminaison exposé et n'effectuer des mises à jour que si elle n'est pas à jour ?

Mon application de démonstration a un tel point de terminaison. Lorsqu'elle est interrogée, elle renvoie la version actuelle de l'application :

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

Remarque :Curieux de connaître la commande HTTP que j'ai exécutée ? Consultez l'article de mon collègue sudoer Jonathan Roemer sur HTTPie.

Je peux utiliser le JSON renvoyé à partir de ce point de terminaison pour prendre des décisions dans mon playbook Ansible. La version précédente de ce playbook exécutait toujours le script de mise à jour de l'application. Cependant, je peux améliorer cela en ne mettant à jour l'application que lorsqu'elle ne répond pas aux exigences de version souhaitée :


---

- 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

Ce playbook présente quelques concepts Ansible utiles. Tout d'abord, vous pouvez voir que le module URI atteint le /api/v1/appVersion Point de terminaison d'API et enregistre la sortie de cet appel URI dans une variable. Les tâches de mise à jour ont été déplacées dans un bloc, ce qui permet le regroupement logique des tâches. L'ajout du quand La clause entraîne l'exécution de ce bloc uniquement si la version actuelle de l'application est différente de la version de l'application souhaitée, telle que renvoyée par /api/v1/appVersion point final. Enfin, j'ai ajouté une vérification supplémentaire au processus de mise à jour. Une fois les mises à jour exécutées, un nouvel appel à /api/v1/appVersion endpoint garantit que la mise à jour a réussi et que la version actuelle de l'application correspond à la version souhaitée. Cela utilise la syntaxe failed_when, qui vous permet de définir des critères d'échec spécifiques pour les tâches.

Exprimée en langage clair, cette logique de bloc Ansible indique :"N'exécutez les scripts de maintenance et d'installation de l'application que si la version actuelle de l'application ne correspond pas à la version souhaitée de l'application. Une fois la mise à jour effectuée, assurez-vous que l'application a bien été mise à jour."

En utilisant seulement quelques lignes de code Ansible, j'ai écrit un moyen puissant mais simple d'utiliser JSON renvoyé par un point de terminaison API pour prendre des décisions intelligentes dans mes playbooks.

Interagir avec un point de terminaison authentifié

Jusqu'à présent, j'ai couvert l'interaction avec les points de terminaison d'API qui ne nécessitent pas d'authentification. Cependant, vous êtes probablement plus habitué à interagir avec des API qui nécessitent un certain type d'authentification, comme un jeton d'API. Le module URI prend en charge cela en définissant les en-têtes et le corps d'une requête HTTP.

[ Vous pourriez également apprécier :9 guides Ansible pour vous aider à vous familiariser avec l'automatisation ]

Je peux aller plus loin dans mon manuel de maintenance en désactivant et en réactivant l'alerte sur chaque hôte de mon système de surveillance. Cela nécessite l'envoi d'un POST demande à un point de terminaison d'API sur le serveur de surveillance. La demande doit contenir mon jeton d'API et l'hôte à l'intérieur du corps encodé en JSON. Ansible rend cela simple. Voici le playbook final :

---

- 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

J'utilise maintenant le module URI pour envoyer HTTP POST requêtes (au lieu de la valeur par défaut GET demandes) à /api/v1/startMaintenance et /api/v1/stopMaintenance points de terminaison sur nyc1-monitoring-1.example.com . Ces demandes contiennent mon jeton API pour le serveur de surveillance dans l'en-tête et le nom d'hôte du serveur est inclus dans le corps. Si l'une des requêtes échoue avec un non-200 code d'état, alors tout le playbook Ansible échoue.

Remarque  :En pratique, vous voudrez utiliser quelque chose comme Ansible Vault pour stocker un jeton d'API, au lieu de le placer directement dans le playbook.

Ce dernier ensemble de tâches me permet d'automatiser complètement mon flux de travail de mise à niveau :effectuer des vérifications de version, interagir avec la surveillance externe pour désactiver l'alerte pour un système et s'assurer que le serveur renvoie les codes d'état HTTP corrects avant et après l'application des correctifs. J'ai maintenant un flux de travail de bout en bout qui automatise bon nombre des étapes courantes que je suis lors de la mise à niveau d'un système.

[ Besoin d'en savoir plus sur Ansible ? Suivez un cours de présentation technique gratuit de Red Hat. Ansible Essentials :Présentation technique de la simplicité dans l'automatisation. ] 

Conclusion

Cet article a commencé avec un simple playbook qui effectuait des vérifications Web de base sur des points de terminaison d'API non authentifiés. Je vous ai expliqué l'analyse des réponses JSON et même l'interaction avec les points de terminaison d'API authentifiés en définissant des en-têtes personnalisés et le contenu du corps sur les requêtes HTTP. Ansible facilite l'interaction avec les services Web, et je suis convaincu que vous trouverez des utilisations pour ce type d'approche, même dans des environnements simples.

Si vous cherchez à en savoir plus et à vous mettre au défi, voici quelques idées à mettre en œuvre par vous-même :

  • Utilisez le module URI pour interagir avec votre service Web préféré basé sur l'API.
  • Voyez si vous savez comment procéder à l'authentification avec des certificats clients.
  • Découvrez comment soumettre un formulaire à un site Web à l'aide d'Ansible.

Linux
  1. Machines virtuelles multipasses à l'aide d'Ansible

  2. Comment créer un utilisateur Linux avec Ansible

  3. Automatiser les requêtes Web à l'aide de Curl ?

  4. Bilan de santé de la page Web à l'aide de Curl ?

  5. Comment effectuer un reniflage de paquets à l'aide de Libpcap avec un exemple de code C

Utilisation de Wget avec FTP pour télécharger/déplacer des sites Web de manière récursive

Comment travailler avec Ansible Provisioner dans Vagrant

Surveiller la tâche Linux avec SysMonTask

Lancer une ferme Web Windows avec Web Deploy

Exemple d'utilisation de getnstimeofday dans le noyau Linux

Utilisation des couleurs avec printf