Use Ansible para interactuar con puntos finales web

Siempre estoy buscando cosas inteligentes que hacer con Ansible. Con tantas herramientas y servicios que aprovechan las interfaces de programación de aplicaciones (API) basadas en HTTP, está claro que interactuar mediante programación con los servicios basados ​​en API de Ansible es una habilidad valiosa. Puede parecer una característica avanzada, pero este artículo le brinda un caso de uso que muestra cómo incluso los entornos simples pueden beneficiarse del poder y la simplicidad del módulo URI de Ansible.

Índice

    Interactuar con puntos finales simples

    Primero, le presentaré un libro de jugadas que aprovecha las capacidades HTTP de Ansible para tomar decisiones inteligentes durante una actualización del servidor web. El libro de jugadas a continuación:

    1. Ejecuta un script de mantenimiento.
    2. Verifica que un punto final de la API Health Check devuelva un HTTP el Servicio 503 no esta disponible por el momento mensaje.
    3. Ejecute un script para actualizar la aplicación.
    4. Ejecuta una secuencia de comandos posterior al mantenimiento para indicar al servidor web que reanude su respuesta normal.
    5. Vuelva a verificar la API de verificación de estado para asegurarse de que responda con 200 bien.

    Aquí está el libro de jugadas:

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

    Estoy usando Ansible módulo URI llegar a /api/v1/healthcheck en el servidor La primera llamada URI espera un HTTP 503 código de estado para devolver ya que el servidor debe estar en modo de mantenimiento y no procesar solicitudes. Después de la actualización, la llamada URI espera un HTTP 200 código de estado, lo que indica que el servidor web está en buen estado nuevamente.

    Este enfoque simple mejora la seguridad de mi libro de jugadas.Si el servidor no puede ingresar al modo de mantenimiento, Ansible no realizará ninguna corrección:

    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 el servidor no vuelve correctamente después de la actualización, Ansible falla con un error:

    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   

    Estos son controles simples que se pueden incorporar en casi cualquier libro de jugadas para agregar mejores garantías de seguridad antes de realizar un trabajo disruptivo o para asegurarse de que el trabajo disruptivo fue exitoso antes de llamarlo éxito.

    El análisis devolvió JSON

    El ejemplo anterior funciona muy bien para controles de salud simples basados ​​en el estado de HTTP. Sin embargo, normalmente deseará recuperar datos de un extremo web y luego hacer algo con los datos devueltos. Por ejemplo: ¿qué pasa si quiero verificar la versión de la aplicación a través de un punto final expuesto y actualizar solo si no está actualizado?

    Mi aplicación de demostración solo tiene ese punto final. Cuando se consulta, devuelve la versión actual de la aplicación:

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

    Notar: ¿Curioso acerca de este comando HTTP que ejecuté? Consulte el artículo de mi colega sudoer Jonathan Roemer sobre HTTPie.

    Puedo usar el JSON devuelto desde este punto final para tomar decisiones en mi libro de jugadas de Ansible. La versión anterior de este libro de jugadas aún ejecutaba el script de actualización de la aplicación. Sin embargo, puedo mejorar esto solo actualizando la aplicación cuando no cumple con los requisitos de la versión deseada:

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

    Este libro de jugadas presenta algunos conceptos útiles de Ansible. Primero, puede ver que el módulo URI llega al /api/v1/appVersion Punto final de la API y registros la salida de esta llamada URI a una variable. Las tareas de actualización se han movido a un bloquear, que permite la agrupación lógica de tareas. La suma de cuando La cláusula hace que este bloque se ejecute solo si la versión actual de la aplicación es diferente de la versión deseada de la aplicación, según lo devuelto por el /api/v1/appVersion punto final. Finalmente, agregué una verificación adicional al proceso de actualización. Una vez que las actualizaciones estén completas, otra llamada a /api/v1/appVersion endpoint garantiza que la actualización se haya realizado correctamente y que la versión actual de la aplicación coincida con la versión deseada. Esto usa el fracaso_cuando sintaxis, que le permite definir criterios de falla específicos para las tareas.

    Expresado en un lenguaje sencillo, esta lógica de bloque de Ansible dice: "Ejecute los scripts de instalación y mantenimiento de la aplicación solo si la versión actual de la aplicación no coincide con la versión deseada de la aplicación". Una vez completada la actualización, asegúrese de que la aplicación se haya actualizado.

    Usando solo unas pocas líneas de código de Ansible, escribí una forma poderosa pero fácil de usar JSON devuelto desde un punto final de API para tomar decisiones inteligentes en mis libros de jugadas.

    Interactuar con un punto final autenticado

    Hasta ahora, he cubierto la interacción con puntos finales de API que no requieren autenticación. Sin embargo, probablemente esté más acostumbrado a interactuar con API que requieren algún tipo de autenticación, como un token de API. El módulo URI se encarga de esto configurando los encabezados y el cuerpo de una solicitud HTTP.

    Puedo ir más allá en mi manual de mantenimiento al deshabilitar y volver a habilitar las alertas en cada host en mi sistema de monitoreo. Esto requiere enviar un PUBLICAR solicitud a un extremo de la API en el servidor de supervisión. La solicitud debe contener mi token API y el host dentro del cuerpo codificado en JSON. Ansible lo hace fácil. Aquí está el libro de jugadas 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

    Ahora estoy usando el módulo URI para enviar HTTP PUBLICAR consultas (en lugar del valor predeterminado HABER solicitudes) a /api/v1/startMaintenance y /api/v1/stopMaintenance puntos finales en nyc1-supervisión-1.ejemplo.com. Estas solicitudes contienen mi token API para el servidor de monitoreo en el encabezado y el nombre de host del servidor está incluido en el cuerpo. Si alguna de las solicitudes falla con un200 código de estado, todo el libro de jugadas de Ansible falla.

    Notar: En la práctica, querrás usar algo como Bóveda de Ansible para almacenar un token de API, en lugar de colocarlo directamente en el libro de jugadas.

    Este último conjunto de tareas me permite automatizar por completo mi flujo de trabajo de actualización: realizar comprobaciones de versión, interactuar con un organismo de control externo para desactivar las alertas de un sistema y asegurarme de que el servidor está devolviendo códigos.Corrija el estado HTTP antes y después de parchear. Ahora tengo un flujo de trabajo integral que automatiza la mayoría de los pasos comunes que realizo al actualizar un sistema.

    Envoltura

    Este artículo comenzó con un libro de jugadas simple que realizó comprobaciones web básicas en puntos finales de API no autenticados. Le expuse cómo analizar las respuestas JSON e incluso cómo interactuar con puntos finales de API autenticados mediante la configuración de encabezados personalizados y contenido del cuerpo en las solicitudes HTTP. Ansible facilita la interacción con los servicios web y estoy seguro de que encontrará usos para este tipo de enfoque, incluso en entornos simples.

    Si está buscando aprender más y desafiarse a sí mismo, aquí hay algunas ideas para que las implemente por su cuenta:

    • Utilice el módulo URI para interactuar con su servicio web basado en API favorito.
    • Vea si puede descubrir cómo autenticarse con certificados de cliente.
    • Descubra cómo enviar un formulario en un sitio web mediante Ansible.

    Artículos de interés

    Subir