Implemente un sitio web estático con Ansible

Una de las mejores maneras de comenzar con una nueva herramienta de automatización es aprovecharla para simplificar algo que ya está haciendo. Muchos administradores de sistemas tienen sus propios sitios web personales. Administrar la implementación de un sitio web estático es una excelente manera de ingresar al mundo de la infraestructura Ansible automatizada y repetible. En este artículo, lo guiaré a través de cómo implementar un sitio web simple en NGINX usando Ansible.

Aunque puede ser tentador usar el oficial Rol de NGINX Ansible por eso, te animo a que lo hagas tú mismo primero. Es una excelente manera de aprender y se sentirá más seguro de sus habilidades de Ansible después de resolver algunos de estos problemas por sí mismo antes de usar las herramientas de nivel superior.

Índice

    Instalar

    Antes de comenzar, déjame darte una descripción general del entorno utilizado para este artículo. Estoy usando una configuración de roles y un diseño de directorio estándar de Ansible como se describe en los documentos

    $ ls
    group_vars/ inventory.ini 
    roles/ site.yml

    Mi archivo de inventario contiene un solo host en un grupo llamado "servidores web", y a este grupo se le asigna una función única en el site.yml:

    $ cat inventory.ini
    [webservers]
    nyc-webserver-1.example.com
    
    $ cat site.yml
    - hosts: webservers
      roles:
        - webserver

    Este artículo describe la creación del rol de servidor web. Antes de sentarse a escribir cualquier tipo de herramienta, es útil considerar los objetivos generales. En este caso, quiero escribir un rol que:

    • Instala y proporciona configuración básica para un servidor web (NGINX)
    • Me permite proporcionar rápidamente la configuración del servidor web para uno o más sitios web, sin tener que profundizar en el código.
    • Implementa el sitio web real en el servidor web. Comenzaré con una implementación simple basada en archivos y luego pasaré a un modelo que aprovecha Git para extraer la última versión de mi sitio.

    ¡Empecemos!

    Instalar y configurar un servidor web

    Lo primero que debe manejar Automatización es instalar y configurar NGINX, mi servidor web elegido. el roles/webserver/tasks/main.yml a continuación se muestra una forma sencilla de lograrlo:

    ---
    - name: Install packages
      package:
        name: "{{ webserver_packages }}"
        state: latest
    
    - name: Add base NGINX configuration file
      copy:
        dest: /etc/nginx/nginx.conf
        src: etc/nginx/nginx.conf
        owner: root
        group: root
        mode: 0644
      notify: Reload NGINX

    Tenga en cuenta que uso el webserver_packages variable para instalar paquetes, en lugar de especificar explícitamente NGINX y otro software. Esta variable facilita agregar o eliminar paquetes de mi función, sin tener que modificar el código real. el webserver_packages la variable se define en el roles/webserver/vars/main.yml archivar:

    ---
    
    webserver_packages:
      - nginx

    La segunda tarea copia una configuración NGINX predeterminada en su lugar. La fuente de esta configuración es el roles/webserver/files/etc/nginx/nginx.conf archivo, que parece un archivo de configuración NGINX predeterminado con algunos cambios menores, como deshabilitar tokens de servidor:

    user nginx;
    worker_processes auto;
    error_log /var/log/nginx/error.log;
    pid /run/nginx.pid;
    
    # Load dynamic modules. See /usr/share/doc/nginx/README.dynamic.
    include /usr/share/nginx/modules/*.conf;
    
    Eventos {
               worker_connections 1024;
    }
    
    http {
               log_format main 
    '$remote_addr - $remote_user [$time_local] "$request" '
                           '$status $body_bytes_sent "$http_referer" '
                           '"$http_user_agent" "$http_x_forwarded_for"';
               access_log /var/log/nginx/access.log main;
               server_tokens off;
               sendfile on;
               tcp_nopush on;
               tcp_nodelay on;
               keepalive_timeout 65;
               types_hash_max_size 2048;
    
               include /etc/nginx/mime.types;
               default_type application/octet-stream;
               # Load modular configuration files from the /etc/nginx/conf.d directory.
               # See http://nginx.org/en/docs/ngx_core_module.html#include
               # for more information.
    
               include /etc/nginx/conf.d/*.conf;
    }

    Tenga en cuenta que no solo puse este archivo en roles/webserver/files/nginx.conf. En su lugar, hice coincidir la ruta de origen con la ruta de destino en el servidor: roles/webserver/files/etc/nginx/nginx.conf. Aunque esto expande un poco su árbol de directorios, es una excelente manera de asegurarse de que sabe dónde van sus archivos y modelos en su host de destino. He descubierto que esta es una táctica organizativa valiosa a lo largo de los años.

    Finalmente, puede ver que la tarea de configuración notifica al controlador de reinicio de NGINX, que se encuentra en roles/webserver/handlers/main.yml:

    - name: Reload NGINX
      service:
        name: nginx
        state: reloaded

    Ejecute este libro de jugadas (usando ansible-playbook -i inventory.ini site.yml) implementa una configuración NGINX muy básica, pero mi servidor aún no está haciendo nada útil. El siguiente paso es configurar los bloques del servidor para servir a cada uno de mis sitios web.

    Implementar bloques de servidor

    Usos de NGINX bloques de servidor para configurar sitios con nombre individuales. Los bloques de servidor son análogos a los hosts virtuales del servidor web Apache. Para que mis sitios web sean accesibles, necesito un bloque de servidor para cada sitio. Puede ser tentador configurar una tarea individual para implementar cada bloque, así:

    - name: Add server block for site1.example.com
      copy:
        src: etc/nginx/conf.d/site1.example.com
        dest: /etc/nginx/conf.d/site1.example.com.conf
    
    - name: Add server block for site2.example.com
      copy:
        src: etc/nginx/conf.d/site2.example.com
        dest: /etc/nginx/conf.d/site2.example.com.conf

    Sin embargo, esto requiere que realice cambios en el código Ansible real que desea agregar a un sitio. También se basa en archivos de configuración estáticos individuales para cada bloque de servidor. Esto parece excesivo y no está realmente en el espíritu de escribir componentes reutilizables que puedan aprovecharse fácilmente en otros proyectos.

    Los patrones y rizos de Ansible proporcionan una excelente manera de lograr esto de forma reutilizable. En lugar de configurar una tarea separada para configurar cada sitio, puedo iterar a través del contenido de una variable y crear un archivo de configuración de plantilla para cada bloque de servidor:

    - name: Add static site config files
      template:
        src: etc/nginx/conf.d/site.conf.j2
        dest: "/etc/nginx/conf.d/{{ item.name }}.conf"
        owner: root
        group: root
        mode: 0644
      with_items: "{{ webserver_static_sites }}"
      notify: Reload NGINX

    Esta tarea utiliza el módulo modelo crear archivos de configuración individuales para cada sitio definido en el webserver_static_sites variable. Esta variable se define en las variables de grupo para el grupo de servidores web en group_vars/webservers.yml:

    webserver_static_sites:
      - name: site1.example.com
        root: /usr/share/nginx/site1.example.com

    Puedes ver que el webserver_static_sites La variable contiene una lista de diccionarios, cada uno de los cuales representa un solo sitio. Todo esto encaja una vez que echa un vistazo a la plantilla utilizada para el archivo de configuración en roles/webserver/templates/etc/nginx/conf.d/site.conf.j2:

    server {
            listen 80;
            listen [::]:80;
    
            server_name {{ item.name }};
            root {{ item.root }};
            index index.html
            server_tokens off;
            charset utf-8;
    
            location / {
                    # First attempt to serve request as file, then
                    # as directory, then fall back to displaying a 404.
                    try_files $uri $uri/ =404;
            }
    }

    Según este patrón, puede ver que la tarea recorre cada entrada en el webserver_static_sites variable. Luego, la tarea crea un archivo de configuración para cada uno con las directivas correspondientes (nombre_servidor y raíz) completadas.

    En este caso, solo tengo un sitio en la lista (site1.example.com). Ejecutar Ansible con esta configuración produce un único /etc/nginx/conf.d/site1.example.com.conf archivo que se ve así (tenga en cuenta que server_name y root se completaron a partir de la plantilla):

    server {
            listen 80;
            listen [::]:80;
            server_name site1.example.com;
            root /usr/share/nginx/site1.example.com;
            index index.html
            server_tokens off;
            charset utf-8;
            location / {
                    # First attempt to serve request as file, then
                    # as directory, then fall back to displaying a 404.
                    try_files $uri $uri/ =404;
            }
    }

    ¡Parece mucho trabajo para un solo sitio! Sin embargo, el poder real de este enfoque reutilizable entra en juego cuando quiero agregar varios sitios. Por ejemplo, considere la versión modificada group_vars/webserver.yml archivo a continuación:

    webserver_static_sites:
      - name: site1.example.com
        root: /usr/share/nginx/site1.example.com
    
      - name: site2.example.com
        root: /usr/share/nginx/site2.example.com
    
      - name: site3.example.com
        root: /usr/share/nginx/site3.example.com

    Sin hacer ningún cambio en mi código, agrego nuevos sitios a mi servidor web simplemente agregando entradas al webserver_static_sites variable. Las ejecuciones posteriores de Ansible implementan toda la configuración necesaria para alojar estos sitios y ahora puedo reutilizar esta función de servidor web en otros proyectos.

    Notará que usé un archivo de configuración NGINX muy básico para cada sitio. Sin embargo, esta plantilla se puede ampliar fácilmente para agregar más directivas de configuración. ¡Pruébelo con algunos de los más comunes que podría usar en la configuración de un sitio web!

    Implementar código

    Si ha seguido hasta aquí, tiene un rol de Ansible que instala y configura NGINX con configuración básica y bloques de servidor. Si bien eso es genial, nadie quiere visitar su sitio si no tiene contenido. A continuación, analizaré dos formas diferentes de implementar contenido en su sitio.

    El primer método para implementar el contenido del sitio es simplemente copiar recursos de su máquina local a su servidor web usando el módulo de copia:

    - name: Copy site contents
      copy:
        dest: "{{ item.root }}/"
        src: "usr/share/nginx/{{ item.name }}/"
        owner: root
        group: root
        mode: 0755
      with_items: "{{ webserver_static_sites }}"

    La tarea anterior itera sobre cada sitio definido en el webserver_static_sites variable y copia el contenido del sitio en roles/webserver/files/usr/share/nginx/{{ item.name }} al destino en el servidor remoto. Si eres nuevo en esto, es un buen comienzo. Sin embargo, esto enreda el código de su aplicación (el contenido de su sitio web) con su sistema de administración de configuración (el repositorio de Ansible). Si bien puede mover la ruta de origen fuera del árbol de directorios de roles de Ansible, este enfoque también asume que todo el código de su sitio web está disponible (y actualizado).

    Una mejor manera sería almacenar su código web en un sistema de administración de código fuente, como Git, y hacer que Ansible implemente su código desde ese repositorio. Dicho almacenamiento garantiza que el código de su servidor web esté siempre actualizado y desacopla por completo la administración de su configuración del código y los recursos del sitio web. Primero, Git debe estar instalado en el servidor web. La instalación es fácil gracias a la separación de paquetes en una variable en el roles/webserver/vars/main.yml archivar:

    webserver_packages:
      - nginx
      - git

    A continuación, reemplace la tarea de copia del ejemplo anterior con la tarea a continuación. Esta tarea usa Ansible Git-módulo para extraer código de un repositorio y colocarlo en el directorio de destino apropiado en el servidor web:

    - name: Clone git repositories
      git:
        repo: "{{ item.repository}}"
        dest: "{{ item.root }}"
        force: yes
      with_items: "{{ webserver_static_sites }}"

    Este paso crea una clave adicional (repositorio) en el diccionario para cada sitio web. el group_vars/webserver.yml El archivo se puede actualizar con esta clave, lo que permite a Ansible extraer código de un repositorio Git remoto:

    webserver_static_sites:
      - name: site1.example.com
        root: /usr/share/nginx/site1.example.com
        repository: https://github.com/acritelli/example-static-site.git

    Tenga en cuenta que si ya implementó el código con el módulo de copia de muestra, deberá eliminar el directorio en el servidor web. Debe hacer esto porque Git no se clonará en un directorio que no esté vacío.

    Pensamientos finales

    Implementar un sitio web simple y estático es una excelente manera de sumergirse en Ansible y ver resultados inmediatos. Esta es una tarea que algunos administradores de sistemas dan por hecho; después de todo, muchos de nosotros alojamos servidores y sitios web privados. Sin embargo, vale la pena tomarse el tiempo para escribir su propio rol de Ansible que permita la implementación rápida de un servidor web. Esto lo ayudará a perfeccionar sus habilidades de Ansible, acortar el tiempo de implementación de su servidor durante la inevitable actualización del sistema operativo y le permitirá escribir un rol reutilizable que se puede usar en otras partes de su infraestructura.

    El código de este artículo está disponible en GitHub.

    Artículos de interés

    Subir