lunes, 11 de septiembre de 2017

Usar Ansible para realizar tareas de administrador de bases de datos.


La orquestación de las tecnologías de la información se ha vuelto importante con el aumento de la escala web (ampliación y descenso de aplicaciones mediante la adición de máquinas virtuales a tareas de uso intensivo de recursos de escala horizontal), para configurar las máquinas recién agregadas sin intervención manual y la gente empieza a usarlo para usarlo para algo más que tareas de aprovisionamiento de máquinas virtuales para aplicaciones web.

El proyecto Ansible decidió hacerlo radicalmente diferente a todos los demás motores. Es diferente en el sentido de que se anuncia como simple, sin agente y poderoso.

Sencillo.

La sencillez es un gran objetivo. Para aquellos de vosotros que habéis trabajado con la configuración / motores de orquestación, hay una curva de aprendizaje pronunciada. Es difícil obtener los principios básicos en la cabeza. Para ser honesto, Ansible también me tomó un tiempo demasiado, para comprender los principios básicos, y obtener la imagen correctamente en mi cabeza. Sin embargo, después de haber trabajado con cfengine comparándolo con los libros de juicio de Ansible, que son los guiones para hacer las cosas en los objetivos, es un soplo de aire fresco. Playbooks son tan limpias que (casi) se puede leer y entender como Inglés simple.

Sin agente

Aquí es donde Ansible es verdaderamente diferente a cualquiera de las otras herramientas de configuración / orquestación. Ansible no requiere ninguna instalación de agente en los objetivos. La siguiente pregunta obvia entonces es: ¿cómo puede esto funcionar? Bueno, muy sencillo: Ansible utiliza ssh para conectarse al host y ejecuta comandos a través del shell. Dicho esto, requiere un poco más de detalle; Ansible utiliza python en el host remoto para su ejecución normal. Sin embargo, puede utilizarlo sin python, por ejemplo para configurar el host para el modo de uso normal Ansible que requiere python y el módulo simple-json.

Esto es realmente importante, y lo convierte en un excelente ajuste para mi trabajo diario como consultor de TI.

Poderoso.

Ansible es poderoso en la forma en que usted puede hacer las tareas de configuración y orquestación de una manera sencilla y limpia.

En resumen, creo que ser sin agente es la verdadera característica asesina aquí. Todos los otros motores de configuración / orquestación requieren que configure y configure una conexión cliente-servidor fija e instale un proceso (demonio) y un servidor central. La autenticación de contraseña ssh o infraestructura de clave pública se puede utilizar.

Debido a que no hay ningún daemon para instalar, puede ejecutar sus playbooks creados en todas partes. Así que en lugar de una configuración fija de cliente, puede crear libros de juego para realizar tareas de rutina y repetirla en varios sitios.

Instalación: añadir EPEL e instalar ansible.

Si usas uno de los clones de RedHat Enterprise Linux (yo uso Oracle Linux), simplemente tiene que agregar el repositorio EPEL (Extra Packages for Enterprise Linux) a su lista de fuentes yum y ejecutar:

 # yum install ansible

Primeros pasos.

Una de las primeras cosas que hago, es crear un directorio para un típico 'proyecto' ansible. Proyecto significa un conjunto de tareas que desea hacer a un conjunto de hosts aquí. A continuación, creo un archivo llamado 'hosts' que es la lista de hosts que desea utilizar para ejecutar tareas en. De forma predeterminada, Ansible busca en / etc / ansible / hosts. En este caso, pongo una sola máquina en ella (una VM de prueba), pero puede ser una lista de máquinas (direcciones IP o nombres de host).

$ cat hosts
192.168.101.2

De hecho, puede crear grupos en el archivo hosts en el "estilo ini". Pero acabo de poner un anfitrión en este ejemplo.
Lo siguiente es comprobar si Ansible lee el archivo correctamente. Esto se hace de la siguiente manera:

$ ansible todos los hosts -i --list-hosts
    192.168.101.2

Bueno, esto significa que Ansible funcionará en este host si se invoca. La siguiente cosa lógica (normalmente cuando está en un nuevo entorno de cliente para comprobar si puede llegar a los hosts):

$ ansible all -i hosts -m ping
192.168.101.2 | FALLA => FALLA: Error en la autenticación.

Ping puede ser un poco engañoso para algunas personas. Lo que ping hace aquí (-m significa módulo), está tratando de conectarse al host sobre ssh, e iniciar sesión. Como no especificó un usuario, utilizó el nombre de usuario del usuario actual en la máquina, que es 'ansible '. Un usuario 'ansible' normalmente no existe en un servidor normal (y no es necesario o debe ser creado), y tampoco en mi servidor de prueba. Así que falló, como decía el mensaje, en la autenticación.

La máquina virtual de prueba es un servidor Linux 6 instalado (OL) básico. Esto significa que sólo hay un usuario: root.
Por lo tanto, vamos a especificar la raíz del usuario como usuario:

$ ansible all -i hosts -m ping -u raíz
192.168.101.2 | FALLA => FALLA: Error en la autenticación.

La autenticación falló de nuevo. Y debería! Qué está haciendo, está intentando abrir una sesión como raíz, y no hemos dado ninguna contraseña, ni he puesto la llave pública de mi usuario local en el archivo authorised_keys alejado. Así que no hay manera de que esto podría funcionar. Esto también suele ser el estado cuando desea hacer cosas con un sistema de cliente "fresco". Vamos a agregar la opción '-k' (preguntar contraseña ssh), y ejecutar de nuevo:

$ ansible all -i hosts -m ping -u root -k
SSH password:
192.168.101.2 | success >> {
    "changed": false,
    "ping": "pong"
}

Para continar: Ahora pide una contraseña, que he llenado, a continuación, lista el anfitrión y el estado: el éxito. Durante esta ejecución, no hubo cambios en el host remoto, y el comando ping dio como resultado un pong (al igual que la respuesta de ping ICMP).
Con lo que hemos aprendido ahora, podemos hacer cosas como esta:

$ ansible all -i hosts -u root -k -a "ifconfig"
SSH password:
192.168.101.2 | success | rc=0 >>
eth0      Link encap:Ethernet  HWaddr 00:0C:29:14:65:ED
          inet addr:192.168.39.145  Bcast:192.168.39.255  Mask:255.255.255.0
          inet6 addr: fe80::20c:29ff:fe14:65ed/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:47 errors:0 dropped:0 overruns:0 frame:0
          TX packets:25 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:6293 (6.1 KiB)  TX bytes:2594 (2.5 KiB)

eth1      Link encap:Ethernet  HWaddr 00:0C:29:14:65:F7
          inet addr:192.168.101.2  Bcast:192.168.101.255  Mask:255.255.255.0
          inet6 addr: fe80::20c:29ff:fe14:65f7/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:188 errors:0 dropped:0 overruns:0 frame:0
          TX packets:112 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:142146 (138.8 KiB)  TX bytes:15545 (15.1 KiB)

lo        Link encap:Local Loopback
          inet addr:127.0.0.1  Mask:255.0.0.0
          inet6 addr: ::1/128 Scope:Host
          UP LOOPBACK RUNNING  MTU:65536  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0
          RX bytes:0 (0.0 b)  TX bytes:0 (0.0 b)

¿Te resulta familiar? Si eres DBA de Exadata claro que sí, esto reproduce parte de la funcionalidad de dcli (aunque dcli está dirigido a ejecutar tareas simples a un grupo de hosts, mientras que Ansible está dirigido a la configuración y orquestación de la empresa).


Playbooks


Ahora vamos a progresar con los Playbooks. Estos Playbooks de Ansible es donde reside la verdadera fuerza de esta tecnología. Permite especificar las tareas que se deben ejecutar en los hosts remotos y crear secuencias de tareas y tomar decisiones basadas en el resultado de las tareas para una ejecución posterior. Permítanme mostrarles un simple libro de jugadas, y guiarlo a través de él:

- hosts: all
  gather_facts: no
  remote_user: root
  tasks:

  - name: upgrade all packages
    yum: name=* state=latest

  - name: install python-selinux
    yum: name=libselinux-python state=installed

  - name: add public key to authorized_key file of root
    authorized_key: user=root state=present key="{{ lookup('file','/home/ansible/.ssh/id_rsa.pub') }}"

Como se puede ver, este es un playbook con tres tareas:

  • Actualizar todos los paquetes
  • Instalar libselinux-python
  • Agregar mi clave pública (local) al archivo de clave autorizado de root (para permitir el acceso sin contraseña).


La línea 1 muestra tres guiones, lo que significa el inicio de un documento YAML.
La línea 2 comienza con un solo guión, que indica una lista. Hay un guión en este nivel de indentación, por lo que es una lista de uno. Los campos de este miembro son hosts, gather_facts y tasks. Tareas tiene su propia lista (la mente del nivel de indención, que es importante). Los campos son pares clave / valor, con la separación indicada por los dos puntos (:). El primer campo es 'hosts', con el valor 'all'. Esto significa que todos los hosts en el archivo de hosts se utilizan para este libro de jugadas. No creo que sea difícil imaginar lo útil que puede ser especificar un grupo / clase de servidores en los que el libro de jugadas puede funcionar. El siguiente es 'gather_facts'. Una ejecución de libro de jugadas normal reúne primero una gran cantidad de información de todos los hosts que va a ejecutar antes de la ejecución. Éstos se pueden utilizar durante la ejecución del libro de jugadas. Siguiente 'remote_user'. Esto indica con qué usuario ansible va a iniciar sesión, por lo que no tenemos que especificarlo en la línea de comandos. Luego vemos 'tareas' para indicar la lista de tareas que se deben ejecutar en los hosts.


Es fácil ver que tenemos tres tareas. Lo que es extremadamente importante, es la identación de esta lista. El nombre no es obligatorio, pero facilita la lectura si se asignan nombres útiles a las tareas y se mostrarán cuando se ejecute el libro de jugadas. La primera tarea tiene el nombre 'actualizar todos los paquetes'. El siguiente campo muestra que la clave es 'yum' indicando que está haciendo uso del módulo yum. Esta clave tiene dos valores: name = *, que significa todos los 'todos los paquetes', y state = latest, lo que significa que queremos que todos los paquetes estén en la última versión. Esto significa que este comando es el equivalente de 'yum update'.

La segunda tarea se llama 'install python-selinux'. Hace uso del módulo del yum otra vez, y se explica por sí mismo, instala el paquete libselinux-python. Estos paquetes son necesarios para trabajar en un host que tiene selinux habilitado en cosas que están protegidas por selinux.

La siguiente tarea se llama 'agregar clave pública al archivo de raíz autorizada'. Se está utilizando el módulo authorized_key. Este módulo requiere un parámetro 'clave', para el cual usamos la función de búsqueda para buscar la clave pública local (!), Del usuario con el que ejecuto ansible, que es 'ansible'. 'Estado = presente' significa que queremos que esta clave esté presente; 'Presente' es el valor por defecto, por lo que no fue necesario ponerlo. Siguiente 'usuario = root': queremos que la clave pública se agregue al archivo authorized_keys de la raíz del usuario.


Por supuesto, estas tareas se pueden ejecutar utilizando el ejecutable 'ansible' como tareas individuales. Para mostrar la importancia de la instalación del módulo libselinux-python en un host con selinux habilitado (que es el estado de selinux en una nueva máquina Oracle Linux instalada), ejecute la tarea usando el módulo authorized_key:

$ ansible all -i hosts -k -u root -m authorized_key -a "user=root state=present key=\"{{ lookup('file','/home/ansible/.ssh/id_rsa.pub') }}\""
SSH password:
192.168.101.2 | FAILED >> {
    "failed": true,
    "msg": "Aborting, target uses selinux but python bindings (libselinux-python) aren't installed!"
}

Claro, ¿no? El anfitrión es selinux protegido. Ahora, vamos a ejecutar la instalación del paquete libselinux como una sola tarea, y luego agregamos nuestra clave pública al archivo authorized_key de root:

$ ansible all -i hosts -k -u root -m yum -a "name=libselinux-python state=installed"
SSH password:
192.168.101.2 | success >> {
    "changed": true,
    "msg": "",
    "rc": 0,
    "results": [
        "Loaded plugins: security\nSetting up Install Process\nResolving Dependencies\n--> Running transaction check\n---> Package libselinux-python.x86_64 0:2.0.94-5.3.el6_4.1 will be installed\n--> Finished Dependency Resolution\n\nDependencies Resolved\n\\n Package             Arch     Version     Repository Size\n\nInstalling:\n libselinux-python   x86_64   2.0.94-5.3.el6_4.1      public_ol6_latest   201 k\n\nTransaction Summary\n\nInstall       1 Package(s)\n\nTotal download size: 201 k\nInstalled size: 653 k\nDownloading Packages:\nRunning rpm_check_debug\nRunning Transaction Test\nTransaction Test Succeeded\nRunning Transaction\n\r  Installing : libselinux-python-2.0.94-5.3.el6_4.1.x86_64  1/1 \n\r  Verifying  : libselinux-python-2.0.94-5.3.el6_4.1.x86_64   1/1 \n\nInstalled:\n  libselinux-python.x86_64 0:2.0.94-5.3.el6_4.1\n\nComplete!\n"
    ]
}
$ ansible all -i hosts -k -u root -m authorized_key -a "user=root state=present key=\"{{ lookup('file','/home/ansible/.ssh/id_rsa.pub') }}\""
SSH password:
192.168.101.2 | success >> {
    "changed": true,
    "key": "ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAliR905hxLnsOCRlOGnmN0H9dGH4NPV88ySC6GMv0KNnU7FfCXYE51Bkk97p2IWFsPhYO9qJDyAFxRm/lia1IZRDpCFcKKKMh5eXmEJC5XSrHWFdmGZRlFcS3VQ3rpCIyU3qFM6xMazh3JHKKEtE1J6nvw/hW3slY9G/6VoJ8CzpfeQMLDOdVXUIcZXqtCPuIEDBQ7yjfMzTGz+hEmz7ImbLaUyB4MDGrDnl33L8mkBEVYu8RrwgBcagDQSiQKnIca/EL45eX/74NG1e/6vxZkHZJz/W0ak4KD+o9vF4ikz0bdrGPMZ5gRYXWoSSHrVA+Rqk8A93qBXNKUUkzGoQYTQ== ansible@ansiblevm.local",
    "key_options": null,
    "keyfile": "/root/.ssh/authorized_keys",
    "manage_dir": true,
    "path": null,
    "state": "present",
    "unique": false,
    "user": "root"
}

Tal vez su cliente no quiere que almacene sus llaves en sus servidores. Es fácil hacer lo contrario y quitar la llave del archivo authorized_key:

$ ansible all -i hosts -u root -m authorized_key -a "user=root state=absent key=\"{{ lookup('file','/home/ansible/.ssh/id_rsa.pub') }}\""
192.168.101.2 | success >> {
    "changed": true,
    "key": "ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAliR905hxLnsOCRlOGnmN0H9dGH4NPV88ySC6GMv0KNnU7FfCXYE51Bkk97p2IWFsPhYO9qJDyAFxRm/lia1IZRDpCFcKKKMh5eXmEJC5XSrHWFdmGZRlFcS3VQ3rpCIyU3qFM6xMazh3JHKKEtE1J6nvw/hW3slY9G/6VoJ8CzpfeQMLDOdVXUIcZXqtCPuIEDBQ7yjfMzTGz+hEmz7ImbLaUyB4MDGrDnl33L8mkBEVYu8RrwgBcagDQSiQKnIca/EL45eX/74NG1e/6vxZkHZJz/W0ak4KD+o9vF4ikz0bdrGPMZ5gRYXWoSSHrVA+Rqk8A93qBXNKUUkzGoQYTQ== ansible@ansiblevm.local",
    "key_options": null,
    "keyfile": "/root/.ssh/authorized_keys",
    "manage_dir": true,
    "path": null,
    "state": "absent",
    "unique": false,
    "user": "root"
}

Por favor, no especificé '-k' en la línea de comandos para enviar una contraseña: en el paso anterior añadimos nuestra clave, para que podamos acceder a nuestro host usando nuestra clave pública. Otra cosa extremadamente importante es 'cambiado'. 'Changed' indica si la tarea realmente cambió algo en el servidor de destino.

He corrido sola tarea hasta ahora, he cambiado el estado de mi VM de prueba de nuevo a su estado antes de empezar a cambiar con ansible (mediante la eliminación del paquete libselinux utilizando 'ansible todo-i hosts -k -u root -m yum -a "Nombre = libselinux-python state = ausente" '


Vamos a ejecutar la playbook descrita anteriormente, el resultado es:

$ ansible-playbook -i hosts -k linux_setup_example.yml
 [WARNING]: The version of gmp you have installed has a known issue regarding
timing vulnerabilities when used with pycrypto. If possible, you should update
it (ie. yum update gmp).

SSH password:

PLAY [all] ********************************************************************

TASK: [upgrade all packages] **************************************************
changed: [192.168.101.2]

TASK: [install python-selinux] ************************************************
changed: [192.168.101.2]

TASK: [add public key to authorized_key file of root] *************************
changed: [192.168.101.2]

PLAY RECAP ********************************************************************
192.168.101.2              : ok=3    changed=3    unreachable=0    failed=0


Ahora en este punto puede ser que pienses: Lo consigo, pero éstas son todas las tareas bastante simples, no es especial en absoluto. Bueno, ahora vamos a describir un caso real que muestra totalmente lo que la importancia de usar esto es, incluso en una sola máquina, pero aún más cuando tienes un gran grupo de servidores que tienes que administrar.

El siguiente ejemplo es una playbook creada para aplicar PSU (Actualizaciones del conjunto de parches) a una Oracle 11.2.0.4. Todavía es bastante simple, sólo se aplica PSU3 al home de Oracle, totalmente automático, ya es bueno tener automatizado mucho trabajo para una sola home, y ahorra un montón de horas (osea: mucho dinero), y te ahorra de error humano.

---
- hosts: all
  vars:
    u01_size_gb: 1
    tmp_size_gb: 1
    oracle_base: /u01/app/oracle
    oracle_home: /u01/app/oracle/product/11.2.0.4/dbhome_1
    patch_dir: /u01/install
  remote_user: oracle
  tasks:

  - name: check u01 free disk space
    action: shell df -P /u01 | awk 'END { print $4 }'
    register: u01size
    failed_when: u01size.stdout|int < {{ u01_size_gb }} * 1024 * 1024

  - name: check tmp free disk space
    action: shell df -P /tmp | awk 'END { print $4 }'
    register: tmpsize
    failed_when: tmpsize.stdout|int < {{ tmp_size_gb }} * 1024 * 1024

  - name: create directory for installation files
    action: file dest={{ patch_dir }} state=directory owner=oracle group=oinstall

  - name: copy opatch and psu
    copy: src=files/{{ item }} dest={{ patch_dir }} owner=oracle group=oinstall mode=0644
    with_items:
     - p6880880_112000_Linux-x86-64.zip
     - p18522509_112040_Linux-x86-64.zip
     - ocm.rsp

  - name: install opatch in database home
    action: shell unzip -oq {{ patch_dir }}/p6880880_112000_Linux-x86-64.zip -d {{ oracle_home }}

  - name: unzip psu patch
    action: shell unzip -oq {{ patch_dir }}/p18522509_112040_Linux-x86-64.zip -d {{ patch_dir }}

  - name: patch conflict detection
    action: shell export ORACLE_HOME={{ oracle_home }}; cd {{ patch_dir }}/18522509; $ORACLE_HOME/OPatch/opatch prereq CheckConflictAgainstOHWithDetail -ph ./
    register: conflict_detection
    failed_when: "'Prereq \"checkConflictAgainstOHWithDetail\" passed.' not in conflict_detection.stdout"

  - name: apply psu
    action: shell export ORACLE_HOME={{ oracle_home}}; cd {{ patch_dir }}/18522509; $ORACLE_HOME/OPatch/opatch apply -silent -ocmrf {{ patch_dir }}/ocm.rsp
    register: apply_psu
    failed_when: "'Composite patch 18522509 successfully applied.' not in apply_psu.stdout"

  - name: clean up install directory
    file: path={{ patch_dir }} state=absent

Espero que esto demuestre lo que Ansible puede ser para un consultor. Este tipo de herramienta es simplemente obligatorio si tienes un entorno con más de aproximadamente diez a veinte servidores para administrar. Ansible puede utilizarse incluso si la organización no quiere dedicar tiempo a la implementación de una herramienta de configuración.