Oracle, Vagrant and Ansible

2018-01-08 1 Di Denis Monari

A great way to experiment Oracle database directly on your laptop in a controlled manner is by using a virtualization tool. One of the best for experimentation is Oracle VirtualBox. Explaining how to use VirtualBox is out of purpose for this post, but it is easy to install and it is available for Windows, Linux, Mac and Solaris. So go download and install it on your laptop.

The most annoying part is every time you need to create your own virtual machine, download the ISO, going through every step to install it, etc. Moreover, what if you need to repeat some (or all) of these steps to retry your tests? Surely you can exploit virtual machine snapshot capabilities, you can save the state of the VM, configuration files, even packages versions, wasting a lot of time. The key word to save time is state; you (always) need to know the state of your machine and software configuration. If you know that state, you can produce a reliable sequence of operations to move your configuration from one state to another: you can automate it, keeping it consistent. It is not sufficient to say “on that machine is installed Oracle database 12.1, with Grid Infrastructure and the latest PSU”. Anyone listening still don’t know where the software is installed, how it is configured, what are its parameters, etc. Every DBA knows how it is annoying to try to understand “how that server is configured..” hoping the next fix won’t broke something because it will collide with (another) fixes someone else have done before.

Two easy tools to help you dive into automation with your own laptop are Vagrant and Ansible. Vagrant is a tool to easly and quickly build a development environment by describing its configuration in a portable text file. Ansible is an agentless automation tool capable to execute tasks on remote machines. Ansible tasks are defined in portable text files, eventually grouped in roles and playbooks. Every role is a way to define a state to be applied by a playbook to (a) specific target(s) machine(s).

Vagrant

Remember the keyword state? We will now try to define the state of a test virtual machine in our laptop so to be able to destroy it and recreate it always identical to itself, in minutes.

You need to download and install both Oracle VirtualBox and Vagrant. After that, create a text file named Vagrantfile in a directory of your choice, with this content:

Vagrant.configure(2) do |config|
 config.vm.box = "centos/7"
 config.vm.synced_folder ".", "/vagrant", disabled: true
 config.ssh.insert_key = false
 config.vm.provider "virtualbox" do |vb|
 vb.memory = "1024"
 vb.cpus = 4
 vb.linked_clone = true
 vb.customize ["modifyvm", :id, "--natdnshostresolver1", "on"]
 end
 config.vm.define "mgmt" do |mgmt|
 mgmt.vm.hostname = "mgmt.ansible.lcl"
 mgmt.vm.network "private_network", ip: "192.168.56.2", virtualbox__hostonly: true
 mgmt.vm.network "public_network"
 end
end

Then, open a command prompt (or a shell), to the same folder where you just saved the Vagrantfile and run the following command:

vagrant up

Vagrant will take care of the rest. It will download a Centos 7 image, it will configure VirtualBox to make it build a VM with 1gb RAM, 4 CPUs and a specific name, a NIC with an IP address valid in the private host-only network (the one where only your laptop and your VM can see each others), a NIC with a NAT configured to have access to your laptop network and NIC configured as a bridge in your laptop network. The VM will have access to your network so it should be able to reach Internet for all updates/downloads need.

All of this should require 5 minutes. After that, by issuing

 vagrant ssh

from the same directory of the Vagrantfile you will connect via ssh to the machine. You will be the user vagrant member of sudoers. Otherwise you can connect to the machien via putty/ssh on port 2222 on localhost or through the private network IP above (but you will need to change the sshd_config file to do so).

When done with your configuration tests, simply disconnect and run

vagrant halt

This will stop the VM (you can always shutdown it from the inside anyway)

If you want to destroy the VM and test again, run this command:

vagrant destroy

and again

vagrant up

to create a new one.

You may create as many VMs as you have resources. Standard rules for same name and IP address remain, so you need to play carefully with your vagrantfiles.

To be able to install Oracle in that VM, we need di adapt the resources because we know Oracle database is resource intensive. Furthermore we may want to use ASM with a dedicated disk.

So, stop the VM and change the Vagrantfile to reflect the following:

Vagrant.configure(2) do |config|
 config.vm.box = "centos/7"
 config.vm.synced_folder ".", "/vagrant", disabled: true
 config.ssh.insert_key = false
 data01_disk = 'C:\oracledb\data01.vdi'
 config.vm.provider "virtualbox" do |vb|
 vb.memory = "4096"
 vb.cpus = 4
 vb.linked_clone = true
 vb.customize ["modifyvm", :id, "--natdnshostresolver1", "on"]
 unless File.exist?(data01_disk)
 vb.customize ['createhd', '--filename', data01_disk, '--size', 10 * 1024]
 vb.customize ['storagectl', :id, '--add', 'sata', '--name', 'SATA Controller']
 vb.customize ['storageattach', :id, '--storagectl', 'SATA Controller', '--port', 3, '--device', 0, '--type', 'hdd', '--medium', data01_disk]
 end
 end
 config.vm.define "mgmt" do |mgmt|
 mgmt.vm.hostname = "mgmt.ansible.lcl"
 mgmt.vm.network "private_network", ip: "192.168.56.2", virtualbox__hostonly: true
 mgmt.vm.network "public_network"
 end
end

We instruct Vagrant to increase the memory to 4gb and add a 10gb disk as a file on the laptop (ready to be used by ASM). Running vagrant up you will notice that vagrant will not provision again your machine (unless you have destroyed it previously) and it will tell you “Run ‘vagrant provision’ or use the ‘–provision’ flag to force provisioning.” Remember this, because you may need to force a provision instead of destroy the VM (and lose the content) and recreate every time.

Logging onto the machine we can see a new /dev/sdb device of about 10gb.

Ansible

So now we can quickly provision a small VM, but we still need to set its state to a known one, maybe with a useful Oracle installation ready to be used.

A full set of Ansible code to install Oracle is way too vast, especially if we need to apply detailed configurations. So I will show how powerfull this combination of tools can be by applying the very first prerequirements.

First we need to create a raw playbook with some tasks to feed Ansible with. Create a text file named playbook.yml with the following content and copy it along the vagrantfile.

---
- hosts: localhost
 become: true
 connection: local

vars:
 - oracle_os_rhel7_deps: [
 binutils.x86_64
 ,compat-libcap1.x86_64
 ,compat-libstdc++-33.i686
 ,compat-libstdc++-33.x86_64
 ,gcc.x86_64
 ,gcc-c++.x86_64
 ,glibc.i686
 ,glibc.x86_64
 ,glibc-devel.i686
 ,glibc-devel.x86_64
 ,ksh
 ,libaio.i686
 ,libaio.x86_64
 ,libaio-devel.i686
 ,libaio-devel.x86_64
 ,libXi.i686
 ,libXi.x86_64
 ,libXtst.i686
 ,libXtst.x86_64
 ,libgcc.i686
 ,libgcc.x86_64
 ,libstdc++.i686
 ,libstdc++.x86_64
 ,libstdc++-devel.i686
 ,libstdc++-devel.x86_64
 ,make.x86_64
 ,nfs-utils.x86_64
 ,sysstat.x86_64
 ]

- oracle_os_common_rhel7_deps: [
 coreutils
 ,dos2unix.x86_64
 ,lsof
 ,lsscsi
 ,parted
 ,perl
 ,screen
 ,unixODBC
 ,unixODBC-devel
 ,unzip
 ,vim
 ,wget
 ,xdpyinfo
 ,xorg-x11-xauth
 ,zip
 ]

tasks:
 - name: selinux update
 lineinfile:
 dest: '/etc/selinux/config'
 regexp: '^SELINUX='
 line: 'SELINUX=disabled'
 state: present
 create: yes
 backup: no

- name: enable login with user and password
 lineinfile:
 dest: '/etc/ssh/sshd_config'
 regexp: '^PasswordAuthentication no'
 line: 'PasswordAuthentication yes'
 state: present
 create: yes
 backup: no
 when: ansible_lsb.major_release|int == 7
 notify:
 - restart sshd

## OS dependencies RHEL 7
 - name: Install common dependencies RHEL 7
 yum: name={{ item }} state=latest
 with_items: "{{ oracle_os_common_rhel7_deps }}"
 when: ansible_lsb.major_release|int == 7

- name: Install Oracle rdbms dependencies RHEL 7
 yum: name={{ item }} state=latest
 with_items: "{{ oracle_os_rhel7_deps }}"
 when: ansible_lsb.major_release|int == 7

handlers:
 - name: restart sshd
 service:
 name: sshd
 state: restarted

Now, Ansible use python indentation rules, so pay carefull attention while copying. I won’t go too much in details, but here what the code will do:

– the playbook will run on localhost with a local connection (on the  same machine where ansible run) and by default exeute the tasks as root.

– the section vars list two variables that contain a list of strings. They are prerequired packages for Oracle database and grid on RHEL 7, plus some useful additions.

– section tasks list four tasks that will be executed sequentially. Module lineinfile will search in a file a specific string using a regular expression and, if found, will change the line. Module yum will execute yum looping through both the above variables and ensuring that each item (package) is updated to the latest release available in the configured repositories.

– the second lineinfile, if its execution change anything inside /etc/ssh/sshd_config file, it will notify the handler restart sshd that will make sshd service restarted (or started if it is stopped).

The beauty of it is the fact that the code is self explanatory.

To be able to run this playbook every time we provision our Vagrant VM, we need to change our vagrantfile one last time. We need to add ansible to the VM itself, otherwise it won’t run (the chicken and the egg) and we need to copy the playbook inside the VM. Here how the vagrantfile become:

Vagrant.configure(2) do |config|
 config.vm.box = "centos/7"
 config.vm.synced_folder ".", "/vagrant", disabled: true
 config.ssh.insert_key = false
 data01_disk = 'C:\oracledb\data01.vdi'
 config.vm.provider "virtualbox" do |vb|
 vb.memory = "4096"
 vb.cpus = 4
 vb.linked_clone = true
 vb.customize ["modifyvm", :id, "--natdnshostresolver1", "on"]
 unless File.exist?(data01_disk)
 vb.customize ['createhd', '--filename', data01_disk, '--size', 10 * 1024]
 vb.customize ['storagectl', :id, '--add', 'sata', '--name', 'SATA Controller']
 vb.customize ['storageattach', :id, '--storagectl', 'SATA Controller', '--port', 1, '--device', 0, '--type', 'hdd', '--medium', data01_disk]
 end
 end
 config.vm.define "mgmt" do |mgmt|
 mgmt.vm.hostname = "mgmt.ansible.lcl"
 mgmt.vm.network "private_network", ip: "192.168.56.2", virtualbox__hostonly: true
 mgmt.vm.network "public_network"
 mgmt.vm.provision "shell" do |s|
 s.inline = "yum install -y epel-release"
 s.privileged = true
 end
 mgmt.vm.provision "shell" do |s|
 s.inline = "yum update -y &&
 yum install -y redhat-lsb-core cifs-utils.x86_64 cifs-utils-devel.i686 cifs-utils-devel.x86_64 libselinux-python python2-pip git ansible docker make rubygem-rake iproute vim-enhanced
 "
 s.privileged = true
 end
 
 mgmt.vm.provision :file, source: "./playbook.yml", destination: "/tmp/playbook.yml"
 mgmt.vm.provision "shell" do |ansible|
 ansible.inline = "ansible-playbook /tmp/playbook.yml"
 end
 end
end

Because we already have provisioned the VM we need to apply the –provision flag while starting up the VM (the provision step is run only the first time the VM is created):

vagrant up --provision

If the machine is stopped, it will startup it and run the provision commands. If the machine is already running, it will run the provision commands.

Adding all the tasks needed to fully install Oracle is a matter of time and work. At the end you will have a playbook describing the state of your installation. The code should be idempotent; if re-executed multiple times against the same machine, it must not break the machine itself and it must converge the configuration to the known state in a safe way.

Happy provisioning! 🙂