Acerca de admin

Tras más de 20 años trabajando con tecnologías Oracle, me decidí a recopilar en un Blog algunas de las cosillas útiles para el día a día.

Instalando el grid infraesturcure con Ansible

Hoy vamos a ver los pasos a llevar a cabo y el codigo para llevar a cabo una instalación de un Grid Infraestructura de la versión 19c en un standalone server .

Como puntos importantes a esta instalación tenemos:

Condiciones

1- Tenemos configurado el sudores del equipo cliente tal y como explicamos en Requisitos de los equipos cliente para automatizar con Ansible: SUDO
2- Vamos a instalar y configurar solamente el GI y el listener, en este paso no vamos a crear en ASM, de esta manera nos va a servir tanto para llevar a cabo una instalación de ASM como para un oracle restart sobre filesystem
3-Tendremos un punto compartido de red donde podremos obtener los binarios descargados de oracle


Para poder ejecutar esta automatización necesitamos:

  • variable env: Nombre del servidor que debe de estar inventariado. En nuestro laboratorio va a ser alone.pamplona.name
  • variable type: Esta variable en nuestro caso va a estar fija a asm
  • Variable version: Variable con el numero de version que queremos instalar,en nuestro laboratorio va a ser la 19.0.0.3

Como veremos, mas adelante , estas tres variables van a ser parseadas enpracticamente todos nuestros playbooks.

Nuestra estructura de Playbooks va a ser muy similar a la de un role de Ansible, nuestros ficheros estarán distribuidos de la siguiente manera


Raiz
|
|- vars:      Ficheros/Inventario de variables
|- templates: Ficheros de templates  
|- file:       Ficheros específicos para  playbooks 

Como veremos mas adelante, el contenido de los directorios Vars y templates va a ser común a todas las ejecuciones, pero el del directorio temp será único por ejecución.

Directorio de variables

Este directorio va a tener los ficheros con información de los estándares que queremos aplicar, por decirlo de una manera sencilla, son los ficheros de nuestras normas

En el lenguaje de Ansible este directorio se podría llamar también inventario de variables

Entr elos ficheros que vamos a usar están:

oracle_standard.yaml

Este fichero va a ser el que contenga los estándares del departamento.
Va a ser común para todas las acciones que se llevan a cabo para las bases de datos de oracle, ya sea instalación de binarios, creación de bases de datos y/o parchados
El ejemplo de nuestro laboratorio es:

[code lang=»py»]
root_user: "root"
oracle_user: "oracle"
oracle_group: "dba"
media_dir: "/mnt/media"
root_directory: "/u01/app"
oracle_inventory: "{{root_directory}}/oraInventory"
oracle_base: "{{root_directory}}/oracle"
stage_directory: "{{oracle_base}}/stage"
oracle_home_directory:
db: "{{oracle_base}}/product/{{version}}/dbhome1"
asm: "{{oracle_base}}/product/{{version}}/grid"
response_file: "{{type}_{{action}}_{{version}}.rsp"
data_location:
FS: "/oradata/{{SID}}/data"
ASM: "+{{oracle_hostname}}_DATA"
fra_location:
FS: "/oradata/{{SID}}/data"
ASM: "+{{oracle_hostname}}_FRA"
redo1_location:
FS: "/oradata/{{SID}}/redo"
ASM: "+{{oracle_hostname}}_REDO1"
redo2_location:
FS: "/oradata/{{SID}}/redo2"
ASM: "+{{oracle_hostname}}_REDO2"
sysasm_passd: changueme2020
asmdbsnmp_passwd: changueme2020

[/code]
Como cosas curiosas podeis ver que

  • Nuestros directorios raices estarán como indican las Oracle Best Practices
  • Cuando la base de datos tenga ASM los diskgroups no se llamaran DATA,REDO y FRA, sino que tendrán como prefijo el nombre del equipo y tendremos dos REDOS
  • Al contrario de las best practices de Oracle vamos ha hacer el taller a lo «old style » todo bajo el grupo dba, el añadir los temas grupos es algo trivial una vez se ha visto la manera en la que funciona el playbook

asm_SO_precondition_19.0.0.3.yaml

La notación de este fichero va a ser $TYPE_SO_precondition_$VERSION.yaml, este este fichero es el que va a ser utilizado para comprobar las precondiciones de la instalación.

En el laboratorio vamos a incluir solamente a modo de ejemplo cinco de paquetes, pero podríamos extenderlo a todo lo que queramos que se compruebe como precondicion de la instalación, como parámetros del kernel, configuraciones del equipo ( selinux, firewall ) …

En caso de tener varias distribuciones de sistemas operativos (asumimos que trabajamos bajo linux) o versiones de los mismos, seria este único fichero el que guardaría las precondiciones tanto de los valores comunes como de los específicos para cada sistema distribución/version

[code lang=»py»]
package_name :
– binutils
– elfutils-libelf
– elfutils-libelf
– oracleasm
– oracleasm-support
[/code]

asm_binaries.yaml

La notación de este fichero va a ser $TYPE_binaries.yaml, aqui tenemos información física del paquete y del response file a usar.
Este fichero no dispone del sufijo version, ya que contendrá a modo de inventario la información de todos los binarios.

[code lang=»py»]
19.0.0.3:
response_file: "asm_binaries_19.0.0.3.rsp"
binary_name: "LINUX.X64_193000_grid_home.zip"
19.0.0.5:
response_file: "asm_binaries_19.0.0.5.rsp"
binary_name: "LINUX.X64_195000_grid_home.zip"[/code]
Otra información adicional que podríamos añadir es el MD5 del binario para comprobar su estado.

Playbooks

En este laboratorio vamos a ejecutar un trabajo en AWX-RPM que va a estar formado por una secuencia de playbooks independientes.
El orden y contenido de cada uno de ellos es:

so_packages

El primer playbook a ejecutar es el que va a comprobar los prerrequisitios de nuestra instalación.
Los chequeos básicos a llevar a cabo son los paquetes y usuarios y grupos

[code lang=»py»]
# Pamplona 2019
# Playbook which checks if required packages ares installed at the destination server
#
# requires
# env: name of the server which should be in the inventory
# type: type of installation [asm|db]
# vars/[type]_SO_precomdition_[version].yaml configuration file with the S.O preconditions

– hosts: "{{env}}"
remote_user: ansible
become: yes
become_user: root
tasks:
– fail: msg="Error no server definied, please define the env variable in the job"
when: env is not defined

– name: Including package
include_vars:
file: "{{type}}_SO_precondition_{{version}}.yaml"

– name: check packages "{{package_name}}"
yum:
name: "{{package_name}}"
state: latest
with_items:
– "{{ package_name }}"

[/code]
Como veíamos en el apartado de variables, carga los paquetes necesarios de un fichero de inventario {{type}}_SO_preconditions_{{version}}.yaml , de esta manera reutilizaremos el mismo fichero para todas las versiones, teniendo que añadir simplemente ficheros de requisitos para las distintas versiones que soportemos.

oracle_directories.yaml

Tenemos un sistema que cumple con los requisitos de configuración de sistema operativo, el siguiente paso es comprobar que nuestra estructura de directorios de oracle existe y en caso contrario crearla.
Para este playbook usaremos el fichero de inventario oracle_standard.yaml en el que , como explicábamos al principio tenemos todos los estándares de nuestra compañía.

[code lang=»py»]
# Pamplona 2019
# Playbook which create the required directories
#
# requires
# env: name of the server which should be in the inventory
# type: type of installation [asm|db]
# version: version of database or asm
# vars/oracle_standard.yaml standard values for Oracle
# ignore if it can ignore errors

– hosts: "{{env}}"
remote_user: ansible
become: yes
become_user: oracle
tasks:
# checking prerrequisites
– fail: msg="Error no server definied, please define the env,type and version variables in the job"
when: env is not defined or version is not defined or type is not defined
# End prerrequisites + check var file for improvement

# Loading env
– name: Including Standard_values
include_vars:
file: "vars/oracle_standard.yaml"

– set_fact:
oracle_home: "{{oracle_home_directory.db}}"
when: type == "db"

– set_fact:
oracle_home: "{{oracle_home_directory.asm}}"
when: type == "asm"

#
– name: create directories
file:
path: "{{ item }}"
state: directory
owner: "{{ oracle_user }}"
group: "{{ oracle_group }}"
mode: ‘0775’
with_items:
– "{{ root_directory }}"
– "{{ stage_directory }}"
– "{{ oracle_inventory }}"
– "{{ oracle_base }}"
– "{{ oracle_home }}"
ignore_errors: "{{ ignore}}"
tags:
– create_directories

[/code]

unzip_binaries.yaml

El propio nombre del playbook lo dice todo.
Vamos a obtener en el equipo cliente el fichero de binarios descargable de Oracle y lo descomprimiremos en la ubiacion que hayamos elegido como $ORACLE_HOME

[code lang=»py»]
# Pamplona 2020
# Playbook which unzips the requested file
#
# requires
# env: name of the server which should be in the inventory
# type: type of installation [asm|db]
# version: version of database or asm
# vars/oracle_standard.yaml standard values for Oracle
# vars/[type]_binaries.yaml configuration file with the variables requested for the extraction
#

– hosts: "{{env}}"
remote_user: ansible
become: yes
become_user: root
tasks:
# checking prerrequisites
– fail: msg="Error no server definied, please define the env and type variable in the job"
when: env is not defined or type is not defined
# Loading env
– name: Including Standard_values
include_vars:
file: "vars/oracle_standard.yaml"

– name: Including binaries info
include_vars:
file: "vars/{{type}}_binaries.yaml"

– name: check for required binaries
stat:
path: "{{media_dir}}/{{version.binary_name}}"
register: exsists
– debug:
msg: The file is availabe
when: exsists.stat.readable == True

– fail:
msg: "ERROR: Source zip file not found"
when: exsists.stat.readable == False
ignore_errors: False

– set_fact:
oracle_home: "{{oracle_home_directory.db}}"
when: type == ‘db’

– set_fact:
oracle_home: "{{oracle_home_directory.asm}}"
when: type == ‘asm’

– name: Extract "{{media_dir}}/{{version.binary_name}} {{ oracle_home }}"
unarchive:
src: "{{media_dir}}/{{version.binary_name}}"
remote_src: yes
dest: "{{ oracle_home }}"
become: yes
become_user: "{{ oracle_user}}"

[/code]

asm_binaries_instal.yaml

Este es el playbook que lleva a cabo la acción real, veamos los pasos que lleva a cabo

  • carga de entornos comprueba los prerrequisitos y carga los ficheros de variables
  • Genera un response file: Este paso es importante ya que, utiliza una template en Jinja para , con las variables obtenidas de nuestro inventario de variables oracle_standard generar un fichero de response en el formato especifico para nuestra version de asm
  • Instalación de binarios: Ejecuta el comando de instalación con el response file generado anteriormente, esta instalación da muchos warnings, entre otras cosas por que , en este laboratorio no estamos cumpliendo todas las Oracle Best practices ( usuarios, grupos, ubicaciones), de ahí que le añadamos la calusula ignore Errors: True
  • Ejecutamos el conocido root.sh
  • Registro del CRS: Hemos instalado e inventariado los binarios, pero el CRS no esta instalado, este paso DEBE de tener ASM, pero, nosotros vamos a llevarlo a cabo sin la necesidad de decirle que tenemos uno, de ahí que lo hagamos ahora en un paso separado , como podemos observar, este paso se lleva a cabo como root, no como usuario oracle
  • Creación de un Listener: Tenemos nuestro CRS instalado y registrado, pero esta vacío, por lo que , vamos a crear un listener bajo esta rama de binarios
  • Habiitar un Listener: Este paso es redundante y da error , ya que el paso anterior lo deja habilitado, simplemente lo ejecutamos como guarda de seguridad
  • Arrancar el Listener: Aunque el objetivo de nuestro laboratorio de hoy era el de tener instalado un cls que funcione, hemos de pensar que , en un futuro crearemos el asm y probablemente instalemos las bases de datos, por lo que, es importante asegurarse que una vez acabamos esta todo encendido

[code lang=»py»]
# Pamplona 2020
# Playbook which sends a respone file and uses it to install Oracle restart/ CRS / Grid on alone server
# requires
# env: name of the server which should be in the inventory
# type: asm or dba
# version: version of database or asm
# vars/oracle_standard.yaml standard values for Oracle
# vars/asm_binaries.yaml: Info with the asm binaries

– hosts: "{{ env }}"
remote_user: ansible
become: yes
become_user: root
tasks:
# checking prerrequisites
– fail: msg="Error no server definied, please define the env variable in the job"
when: env is not defined or version is not defined or type is not defined
# Loading env
– name: Including Standard_values
include_vars:
file: "vars/oracle_standard.yaml"

– name: Including binaries info
include_vars:
file: "vars/{{type}}_binaries.yaml"
– set_fact:
oracle_home: "{{oracle_home_directory.db}}"
when: type == ‘db’

– set_fact:
oracle_home: "{{oracle_home_directory.asm}}"
when: type == ‘asm’
#
– name: generate response file
template:
src: "templates/{{ version.response_file }}.j2"
dest: "{{ stage_directory }}/{{version.response_file}}"
mode: ‘0644’

– name: Install Grid 19c Software
shell: "{{ oracle_home }}/gridSetup.sh -silent -responseFile {{ stage_directory }}/{{version.response_file}}"
become: yes
become_user: "{{ oracle_user }}"
args:
chdir: "{{ oracle_home }}"
ignore_errors: True

– name: execute root.sh
shell: "{{ oracle_home }}/root.sh"
become: yes
become_user: root
args:
chdir: "{{ oracle_home }}"

– name: Configure CRS
shell: $GI_HOME/perl/bin/perl -I $GI_HOME/perl/lib -I $GI_HOME/crs/install $GI_HOME/crs/install/roothas.pl
become: yes
become_user: root
args:
chdir: "{{ oracle_home }}"
environment:
GI_HOME: "{{ oracle_home }}"
ignore_errors: True

– name: Create listener
shell: "$GI_HOME/bin/srvctl add listener -listener LISTENER -oraclehome {{ oracle_home }}"
become: yes
become_user: "{{oracle_user}}"
args:
chdir: "{{ oracle_home }}"
environment:
GI_HOME: "{{ oracle_home }}"
ORACLE_BASE: "{{ oracle_base}}"
ignore_errors: False

– name: Enable listener
shell: $GI_HOME/bin/srvctl enable listener -listener LISTENER
become: yes
become_user: "{{oracle_user}}"
args:
chdir: "{{ oracle_home }}"
environment:
GI_HOME: "{{ oracle_home }}"
ORACLE_BASE: "{{ oracle_base}}"
ignore_errors: True

– name: Start listener
shell: $GI_HOME/bin/srvctl start listener -listener LISTENER
become: yes
become_user: "{{oracle_user}}"
args:
chdir: "{{ oracle_home }}"
environment:
GI_HOME: "{{ oracle_home }}"
ORACLE_BASE: "{{ oracle_base}}"
ignore_errors: False

[/code]

Como siempre, las ultimas versiones de este codigo están disponibles en el proyecto provisioning en GITHUB

Sintaxsis basica de Ansible

Los playbooks de Ansible están codificados en YAML , para poder comprender mejor la siguiente tanda de post, vamos a ver muy muy de pasada la sintaxis básica de ansible.

Antes de empezar, tener claro que ,si seguimos al pie de la letra el documento de formación de RedHat Automation with Ansible 2.0 DO407 tenemos que Ansible NO funciona con Pyton 3. EL nodo de control debe de tener Pyton 2.6 o 2.7 . Pero ,el sistema en el que esta instalado el tower tiene un pyton 3, con lo que habra que coger la documentacion con pinzas

Apertura y cierre del playbook

En Yaml el código se abre con tres guiones consecutivos
[code lang=»py»]

[/code]

Cadenas

Las cadenas en Yaml no necesitan comillas, puedes incluirlas con comillas simple, debe o sin comillas, y funcionaria en los tres casos

[code lang=»py»]

‘ Esto es una cadena ‘
"Esto es una cadena "
Esto es una cadena
[/code]

Cadenas multilinea

Al no ser necesario el uso de comillas para las cadenas la primera pregunta que nos viene a la cabeza es ¿como ponemos las cadenas multilinea?
La respuesta es muy sencilla, tenemos dos maneras

– Mediante el caracter |
[code lang=»py»]

msg: |
Esto es una cadena
de mas de dos lineas
y mas de tres
[/code]

– Mediante el caracter >
[code lang=»py»]
msg: >
Esto es una cadena
de mas de dos lineas
y mas de tres
[/code]

Listas

Las listas en Ansible vienen delimitadas por el caracter
[code lang=»py»]

– perro
– gato
– tortuga
– canario
[/code]

También pueden configurarse delimitadas por comas entre
[code lang=»py»]
animales:
[ perro, gato , tortuga , canario ]
[/code]

Comentarios

Los comentarios al igual que en la programación bash se hacen con el caracter #

[code lang=»py»]
# Esto es un comentario
[/code]

Chequeo de sintaxis

Al ser un lenguaje interpretado o hay compilación, pero si que puedes comprobar si la sintaxis es correcta.

La comprobación puede llevarse a cabo directamente con el pyton con el comando

python -c 'import yaml, sys; print yaml.load(sys.stdin)' < playbook.yml

Pero , la mas sencilla para nosotros y mas aconsejable si estamos trabajando con ansible es hacerlo mediante el flan --syntax-check del comando ansible-playbook

ansible-playbook --syntax-check  playbook.yml

Uso de variables en Ansible

Vamos a intentar recuperar poco a poco otra de las entradas que se borraron en enero, el uso de las variables en Ansible.

El primer punto a tener en cuenta es que tal y como indica la Documentacion de ansible las variables deben de ser letras, numeros y underscores, y siempre deben de comenzar por ua letra.

Asi pues, no debemos de usar nombres de variables que comienzen por numeros, contengan puntos , sean numeros o contengan guiones.

Vamos a ver un ejemplo de definicion de variables sobre el que trabajaremos
[code lang=»py»]
oracle_user: "oracle"
root_directory: "/u01/app"
oracle_inventory: "{{root_directory}}/oraInventory"
oracle_base: "{{root_directory}}/oracle"
stage_directory: "{{oracle_base}}/stage"
oracle_hostname: "{{ ansible_hostname|upper }}"
data_location:
FS: "/oradata/{{SID}}/data"
ASM: "+{{oracle_hostname}}_DATA"
package_name :
– binutils
– oracleasm
– oracleasm-support
– compat-libcap1
– compat-libstdc++-33
– elfutils-libelf-devel
huge_pages: "{{((0.6 * ansible_memtotal_mb)*1024/2)|round|int }}"
kernel_values:
– { name: fs.file-max, value: 6815744 }
– { name: kernel.sem, value: "250 32000 100 128" }
– { name: kernel.shmmni, value: 4096 }
firewalld:
state: stopped
enabled: no

[/code]