Como eterno aprendiz siempre estoy intentando mejorar mis scripts, procesos o flujos de trabajo. Llevo tiempo leyendo sobre SRE y me llama la atención los pensamientos que se tienen en este rol sobre automatización, pruebas, metodologías, etc. He decidido empezar aprendiendo Ansible y aplicarlo en mi día a día como DBA.
No se me ha ocurrido mejor manera para practicar que implementarlo en tareas repetitivas. Es por eso que en este post voy a pasar los prerrequisitos al SO utilizando ansible antes de instalar GRID, ASM y el RDBMS 19c.
Ansible
Lo primero que he querido aprender es como funciona ansible. Ansible se instala y configura en máquina controladora , en mi caso en mi portátil macOS desde donde gestiono todo mi laboratorio. Desde esta máquina controladora se conecta a las máquinas donde se quieren realizar las configuraciones, que son los nodos gestionados.
Para instalar ansible en macOS he utilizado brew.
brew install ansible
ansible --version
Requisitos
Para conseguir que todo funcione correctamente he necesitado python y ssh en ambas máquinas.
Lo primero ha sido crear las claves ssh para poder acceder sin utilizar password en las conexiones.
ssh-keygen -t rsa -b 4096 -C "ansible-controller"
ssh-copy-id root@<IP-MAQ-DESTINO>
ssh root@<IP-MAQ-DESTINO> "hostname"
En la máquina destino he tenido que instalar python 3.9, ya que la versión que venía con el sistema Oracle Linux 8.10 me estaba dando problemas en las pruebas realizadas.
dnf install python39 -y
Preparación de la magia
En la maquina controladora he creado los siguientes directorios para ir generando los ficheros .yml.
mkdir -p ~/Documents/oracle-iac/ansible
cd ~/Documents/oracle-iac/ansible
mkdir -p inventories/dev/group_vars
mkdir -p roles/oracle_prereqs/{tasks,defaults,templates}
mkdir -p playbooks
Para verlo más gráficamente añado el arbol de directorios resultante:
~/Documents/oracle-iac/ansible
├── inventories
│ └── dev
│ └── group_vars
├── playbooks
└── roles
└── oracle_prereqs
└── tasks
└── defaults
└── templates
Para que la magia suceda tengo que indicarle a ansible algunas cosas en ficheros de configuración.
Fichero de configuración ansible.cfg
Con este fichero estoy indicando a ansible donde está el inventario de las máquinas a las que se tiene que conectar, que usuario utilizar y como escalar privilegios.
cat > ~/Documents/oracle-iac/ansible/ansible.cfg << 'EOF'
[defaults]
inventory = inventories/dev/hosts.yml
remote_user = oracle
host_key_checking = False
roles_path = roles
result_format = yaml
[privilege_escalation]
become = True
become_method = sudo
become_user = root
EOF
Fichero de configuración hosts.yml
En este fichero le enseño a ansible cual es mi infraestructura, en este caso le indico la máquina destino donde quiero hacer las configuraciones. Se pueden tener servidores en diferentes entornos como dev, pre y pro por ejemplo. Al estar aprendiendo, de momento yo utilizo entorno dev.
cat > ~/Documents/oracle-iac/ansible/inventories/dev/hosts.yml << 'EOF'
---
all:
children:
oracle_servers:
hosts:
db-server-01:
ansible_host: <IP-MAQ-DESTINO>
ansible_user: root
ansible_ssh_private_key_file: ~/.ssh/id_rsa
EOF
Antes de continuar explicando a ansible lo que quiero que haga en esa máquina voy a comprobar que es capaz de conectarse a ella.
ansible oracle_servers -m ping
Si todo va bien, contestará con un ping-pong.
db-server-01 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python3.9"
},
"changed": false,
"ping": "pong"
}
Fichero de variables
Fichero donde indico los directorios y usuarios.
cat > ~/Documents/oracle-iac/ansible/inventories/dev/group_vars/oracle_servers.yml << 'EOF'
---
# ============================================================
# USUARIOS Y GRUPOS ORACLE
# ============================================================
oracle_user: oracle
grid_user: grid
oracle_group: oinstall
# ============================================================
# DIRECTORIOS BASE
# ============================================================
oracle_base: /u01/app/oracle
grid_base: /u01/app/grid
oracle_inventory: /u01/app/oraInventory
grid_home: /u01/app/19.3.0/grid
oracle_home: /u01/app/oracle/product/19.3.0/dbhome_1
EOF
En este fichero especifico los id para los diferentes usuarios y grupos que voy a generar a continuación.
cat > ~/Documents/oracle-iac/ansible/roles/oracle_prereqs/defaults/main.yml << 'EOF'
---
swap_size_mb: 8192
oracle_user_uid: 54321
grid_user_uid: 54331
oinstall_gid: 54321
dba_gid: 54322
oper_gid: 54323
asmdba_gid: 54327
asmoper_gid: 54328
asmadmin_gid: 54329
EOF
Ficheros donde indico los profiles a modo plantilla para que los genere en el destino.
cat > ~/Documents/oracle-iac/ansible/roles/oracle_prereqs/templates/oracle_bash_profile.j2 << 'EOF'
# Oracle User Environment
export ORACLE_BASE={{ oracle_base }}
export ORACLE_HOME={{ oracle_home }}
export ORACLE_SID={{ db_name }}
export PATH=$ORACLE_HOME/bin:$PATH
export LD_LIBRARY_PATH=$ORACLE_HOME/lib:/lib:/usr/lib
export CLASSPATH=$ORACLE_HOME/jlib:$ORACLE_HOME/rdbms/jlib
export NLS_DATE_FORMAT="DD-MON-YYYY HH24:MI:SS"
export NLS_LANG=AMERICAN_AMERICA.AL32UTF8
# Alias útiles
alias sqlp='sqlplus / as sysdba'
alias alert='tail -200f $ORACLE_BASE/diag/rdbms/$(echo $ORACLE_SID | tr [:upper:] [:lower:])/$ORACLE_SID/trace/alert_$ORACLE_SID.log'
EOF
cat > ~/Documents/oracle-iac/ansible/roles/oracle_prereqs/templates/grid_bash_profile.j2 << 'EOF'
# Grid Infrastructure User Environment
export ORACLE_BASE={{ grid_base }}
export ORACLE_HOME={{ grid_home }}
export PATH=$ORACLE_HOME/bin:$PATH
export LD_LIBRARY_PATH=$ORACLE_HOME/lib:/lib:/usr/lib
# Alias útiles
alias crsstat='crsctl stat res -t'
alias asmcmd='asmcmd'
EOF
Fichero de tareas main.yml
Este fichero es donde indico todo lo que quiero que se haga de manera automática.
cat > ~/Documents/oracle-iac/ansible/roles/oracle_prereqs/tasks/main.yml << 'EOF'
---
# ============================================================
# TAREA 1: Instalar el preinstall de Oracle Linux
# ============================================================
- name: "PREREQS | Instalar oracle-database-preinstall-19c"
ansible.builtin.command: >
dnf install -y
oracle-database-preinstall-19c
tags: prereqs
# ============================================================
# TAREA 2: Crear grupos del sistema para Oracle
# ============================================================
- name: "PREREQS | Crear grupos Oracle"
ansible.builtin.group:
name: "{{ item.name }}"
gid: "{{ item.gid }}"
state: present
loop:
- { name: oinstall, gid: "{{ oinstall_gid }}" }
- { name: dba, gid: "{{ dba_gid }}" }
- { name: oper, gid: "{{ oper_gid }}" }
- { name: asmdba, gid: "{{ asmdba_gid }}" }
- { name: asmoper, gid: "{{ asmoper_gid }}" }
- { name: asmadmin, gid: "{{ asmadmin_gid }}" }
tags: prereqs
# ============================================================
# TAREA 3: Crear usuario oracle
# ============================================================
- name: "PREREQS | Crear usuario oracle"
ansible.builtin.user:
name: oracle
uid: "{{ oracle_user_uid }}"
group: oinstall
groups: "dba,oper,asmdba"
home: /home/oracle
shell: /bin/bash
comment: "Oracle Software Owner"
state: present
tags: prereqs
# ============================================================
# TAREA 4: Crear usuario grid
# ============================================================
- name: "PREREQS | Crear usuario grid"
ansible.builtin.user:
name: grid
uid: "{{ grid_user_uid }}"
group: oinstall
groups: "asmdba,asmoper,asmadmin"
home: /home/grid
shell: /bin/bash
comment: "Oracle Grid Infrastructure Owner"
state: present
tags: prereqs
# ============================================================
# TAREA 5: Crear la estructura de directorios
# ============================================================
- name: "PREREQS | Crear directorios Oracle"
ansible.builtin.file:
path: "{{ item.path }}"
state: directory
owner: "{{ item.owner }}"
group: oinstall
mode: "0755"
loop:
- { path: "/u01", owner: root }
- { path: "/u01/app", owner: oracle }
- { path: "{{ oracle_inventory }}", owner: oracle }
- { path: "{{ oracle_base }}", owner: oracle }
- { path: "{{ oracle_home }}", owner: oracle }
- { path: "{{ grid_base }}", owner: grid }
- { path: "{{ grid_home }}", owner: grid }
- { path: "{{ sw_stage_dir }}", owner: root }
tags: prereqs
# ============================================================
# TAREA 6: .bash_profile para usuario oracle
# ============================================================
- name: "PREREQS | Configurar .bash_profile de oracle"
ansible.builtin.template:
src: oracle_bash_profile.j2
dest: /home/oracle/.bash_profile
owner: oracle
group: oinstall
mode: "0644"
tags: prereqs
# ============================================================
# TAREA 7: .bash_profile para usuario grid
# ============================================================
- name: "PREREQS | Configurar .bash_profile de grid"
ansible.builtin.template:
src: grid_bash_profile.j2
dest: /home/grid/.bash_profile
owner: grid
group: oinstall
mode: "0644"
tags: prereqs
# ============================================================
# TAREA 8: Deshabilitar SELinux
# ============================================================
- name: "PREREQS | Poner SELinux en permissive (runtime)"
ansible.builtin.command: >
setenforce Permissive
tags: prereqs
- name: "PREREQS | Poner SELinux en permissive (persistente)"
ansible.builtin.replace:
path: /etc/selinux/config
regexp: '^SELINUX=.*'
replace: 'SELINUX=permissive'
tags: prereqs
# ============================================================
# TAREA 9: Deshabilitar el firewall
# ============================================================
- name: "PREREQS | Parar firewall"
ansible.builtin.command: >
systemctl stop firewalld
tags: prereqs
- name: "PREREQS | Deshabilitar firewall"
ansible.builtin.command: >
systemctl disable firewalld
tags: prereqs
EOF
Donde sucede la magia, el playbook
Una vez que he preparado todo indicando variables, usuarios, etc es hora de decirle a ansible que quiero que me ejecute y donde. Para ello se necesita ejecutar un playbook.
Al ser de prerrequisitos lo he llamado 01_prereqs.yml
cat > ~/Documents/oracle-iac/ansible/playbooks/01_prereqs.yml << 'EOF'
---
- name: "PLAYBOOK 1 | Configurar prerequisitos Oracle en el SO"
hosts: oracle_servers
become: true
roles:
- role: oracle_prereqs
tags: prereqs
EOF
Para ejecutar el playbook utilizo -vv para que haga el verbose y tags como buena practica para que ejecute lo que he puesto con esos tags.
ansible-playbook playbooks/01_prereqs.yml -vv --tags prereqs
Nota: Los tags son heredados por los roles, es decir, si dentro de un rol tengo varios tags, al ejecutar el rol ejecutará todo lo que hay por debajo.
Lo que me ha servido para aprender esto es ver los tags que tiene cada tarea ansible-playbook playbooks/01_prereqs.yml --list-tasks
A modo de ejemplo añado la salida “recortada” que he obtenido tras ejecutar esto.
ansible-playbook playbooks/01_prereqs.yml -vv --tags prereqs
ansible-playbook [core 2.20.4]
config file = /Users/jesus/Documents/oracle-iac/ansible/ansible.cfg
PLAYBOOK: 01_prereqs.yml ***********************************************************************************************************************************************************
1 plays in playbooks/01_prereqs.yml
PLAY [PLAYBOOK 1 | Configurar prerequisitos Oracle en el SO] ***********************************************************************************************************************
TASK [Gathering Facts] *************************************************************************************************************************************************************
task path: /Users/jesus/Documents/oracle-iac/ansible/playbooks/01_prereqs.yml:2
ok: [db-server-01]
TASK [oracle_prereqs : PREREQS | Instalar oracle-database-preinstall-19c] **********************************************************************************************************
task path: /Users/jesus/Documents/oracle-iac/ansible/roles/oracle_prereqs/tasks/main.yml:7
changed: [db-server-01] => {"changed": true, "cmd": ["dnf", "install", "-y", "oracle-database-preinstall-19c"], "delta": "0:00:02.603811", "end": "2026-04-10 14:13:39.720259", "msg": "", "rc": 0, "start": "2026-04-10 14:13:37.116448", "stderr": "", "stderr_lines": [], "stdout": "Last metadata expiration check: 1:49:47 ago on Fri 10 Apr 2026 12:23:50 PM WEST.\nPackage oracle-database-preinstall-19c-1.0-2.el8.x86_64 is already installed.\nDependencies resolved.\nNothing to do.\nComplete!", "stdout_lines": ["Last metadata expiration check: 1:49:47 ago on Fri 10 Apr 2026 12:23:50 PM WEST.", "Package oracle-database-preinstall-19c-1.0-2.el8.x86_64 is already installed.", "Dependencies resolved.", "Nothing to do.", "Complete!"]}
.
.
.
TASK [oracle_prereqs : PREREQS | Deshabilitar firewall] ****************************************************************************************************************************
task path: /Users/jesus/Documents/oracle-iac/ansible/roles/oracle_prereqs/tasks/main.yml:135
changed: [db-server-01] => {"changed": true, "cmd": ["systemctl", "disable", "firewalld"], "delta": "0:00:00.081855", "end": "2026-04-10 14:13:48.866159", "msg": "", "rc": 0, "start": "2026-04-10 14:13:48.784304", "stderr": "", "stderr_lines": [], "stdout": "", "stdout_lines": []}
PLAY RECAP *************************************************************************************************************************************************************************
db-server-01 : ok=12 changed=4 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
En este punto pensaba que era muy bonito para ser verdad. Tenía que comprobar que efectivamente había hecho cosas. Me conecté al servidor y comprobé los usuarios y la modificación del SELinux.
[root@localhost ~]# id oracle
uid=54321(oracle) gid=54321(oinstall) groups=54321(oinstall),54322(dba),54323(oper),54327(asmdba)
[root@localhost ~]# id grid
uid=54331(grid) gid=54321(oinstall) groups=54321(oinstall),54327(asmdba),54328(asmoper),54329(asmadmin)
[root@localhost ~]# cat /etc/selinux/config | grep -i "SELINUX="
# SELINUX= can take one of these three values:
SELINUX=permissive
Y esto me lleva a pensar la de horas que he utilizado para preparar diferentes servidores, nodos de cluster como dba tradicional.
Como eterno aprendiz, ahora que estoy aprendiendo ansible no solo ahorraré tiempo al hacer despliegues o configuraciones en diferentes nodos. También reduciré la posibilidad de errores en tareas repetitivas.
Cuando el DBA tradicional empieza a integrar conceptos de SRE nace el DBRE.
Ser DBA es cosa del pasado.
DBA + SRE = DBRE