vagrant and ansible

... putting vagrant on steroids ...

Posted by geoffm on Monday, January 1, 0001

vagrant and ansible

putting vagrant on steroids

First, set up vagrant to create a group of guest virtual machines (see previous posts). We will assume your Vagrantfile is configured to build/start 3 nodes (node1, node2, node3). If these are bare builds you will want to personalize or customize each of them.

Chances are you will want to give each of them a common base of applications and configuration. As a significant example, downloaded images are set for universal time; not what you want. It is simple to just edit the /etc/timezone file or write a script to change it on multiple machines. But if you have lots of machines or tear these machines down and stand them back up frequently then exercises like this will get very tedious. Additionally there are and handful of packages you typically want one every machine.

The provision tool named ansible was designed by the author to be “a tool that I could not use for 6 months, come back later, and still remember how it worked”. The advantage for me is that it leverages ssh and sudo, tools you use everyday. It uses YAML files for configuration Wikipedia YAML which, once you understand the syntax, is easy to read and manage.

Using vagrant is a great way to consistently build virtual nodes. Add ansible to the mix and you have a great way to build and maintain many usable nodes. The vagrant tool uses ansible without any additional work other than properly calling it within your default config file (Vagrantfile). Throw a few lines in to have the ansible-playbook tool call a playbook.yml file and the fun begins.

eg: Within your Vagrantfile (see earlier post on vagrant-lxc)

  config.vm.provision :ansible do |ansible|
    ansible.playbook = "playbook.yml"
  end

The other half of the story is ansible and the playbook.yml config file. Install ansible (a python based tool) with this (assumes a Debian family OS):

sudo apt-get install ansible

Now lets write the playbook.yml file and put it right where your Vagrantfile is.

A quick word about ansible. DevOps is the buzzword of recent IT deployment trend. The driving focus is rolling continuous (frequent) deployments and upgrades. The underlying theory is that any weaknesses in your deployment strategy is exposed early and it keeps human hands off the controls. Here, we will take advantage of that perspective. The ansible suite comes with half dozen tools which might throw you off initially. The only one you concern yourself with (early in the relatively smooth learning curve) is ansible-playbook - forget about the others for now (ie ansible, ansible-doc, ansible-galaxy, ansible-pull, ansible-tools, ansible-vault). The tool ansible-playbook is designed to read a YAML (with the yml extension) playbook file (it can be called anything as long as it is a .yml file). It absorbs and executes the tasks and plays out these commands against specified hosts or host groups. That was a over simplification because it conceals the depth and feature rich possibilities of ansible playbooks. That is for another time perhaps.

Here is a quick-n-dirty playbook.yml to call from a Vagrantfile: There are a lot of commented lines of code ready for you to explore on your own.

Note: ansible YAML (xxx.yml) playbook files start with three dashes - hence --- at the beginning. One critical syntax element to know is that left alignment is essential for YAML to know the subordinate level for processing.

---
################
# NOTE: assumes:
#       This all resides in
#       $HOME/vagrant
#################
  • hosts: all vars:

    • docroot: /var/www/html
    • domain: my.lab
    • ntp_timezone: America/New_York
    • user_password: "$6$SomeSalt$bUGZDQdxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx65N/BhPuCfeg8eE1zqc8VNRFYB9sfX1"

      python -c ' import crypt; print crypt.crypt("This is my Password", "$6$SomeSalt$") '

    sudo: true tasks:

    • name: Visually show host basics debug: msg='host [ {{ ansible_hostname }} ] {{ ansible_all_ipv4_addresses }} arch [ {{ ansible_architecture }} ]'

    • name: install required packages for apt upgrade apt: name={{ item }} state=latest update_cache=true with_items:

      • python-apt
      • aptitude
    • name: apt upgrade apt: upgrade=yes update_cache=yes dpkg_options='force-confold,force-confdef'

    • name: install required packages apt: name={{ item }} state=latest update_cache=true with_items:

      • vim
      • lynx
      • screen
      • git
      • ufw
      • rsync
      • chkrootkit
      • curl
      • alpine

- python3-pip

- inxi

- virtualbox-guest-utils

- name: Install postfix
  apt: name=postfix state=latest
  register: postfix_installed
                  
- name: Add inet_protocols = ipv4 line to main.cf
  lineinfile: dest=/etc/postfix/main.cf line='inet_protocols = ipv4'
          
- name: Install ntp
  apt: name=ntp state=installed
  when: ansible_os_family == 'Debian'
              
- name: Ensure proper timezone
  file: src=../../../usr/share/zoneinfo/{{ ntp_timezone }} dest=/etc/localtime state=link force=yes

- name: Install admin packages

apt: name={{ item }} state=present

with_items:

- nmap

- lsof

- make

- name: Install MySQL client server and related libraries

apt: name={{ item }} state=latest

with_items:

- mysql-client

- mysql-server

notify:

- Start mysql server

- name: Install PHP and its modules

apt: pkg={{ item }} state=latest

with_items:

- php5

- php5-cli

- php5-curl

- php5-gd

- php5-imagick

- php5-mysql

- php5-xmlrpc

- name: Install Apache and its module9(s)

apt: pkg={{ item }} state=latest

with_items:

- apache2

- libapache2-mod-php5

register: apacheinstalled

- name: Activate mod_rewrite

apache2_module: name=rewrite state=present

notify:

- Start apache server

- name: Create web root

when: apacheinstalled|success

file: dest=/var/www/html mode=775 state=directory owner=www-data group=www-data

notify:

- Reload apache

# make sure admin group exists
- name: assure admin group
  group: name=admin state=present

# Add the user 'wocos'
- name: Add user wocos
  user: name=wocos groups=sudo,admin
    shell=/bin/bash 
    generate_ssh_key=yes 
    ssh_key_file=.ssh/id_rsa 
    createhome=yes state=present
    password="{{user_password }}"
    # created with:
    # python -c ' import crypt; print crypt.crypt("This is my Password", "$6$SomeSalt$") '
    # the var user_password is defined above in the vars section

    # another interactive way to add a password
    # vars_prompt:
    # - name: "user_password"
    #   prompt: "Enter a password for the user: "
    #   private: yes
    #   encrypt: "md5_crypt" # needs python-passlib installed
    #   confirm: yes
    #   salt_size: 7
    # password="{{ user_password }}"

handlers:

- name: Start mysql server

service: name=mysql state=started enabled=yes

- name: Start apache

service: name=apache2 state=restarted enabled=yes

- name: Reload apache

service: name=apache2 state=reloaded

######## EOF ##########

Call this from within your Vagrantfile as noted above (please see previous post on vagrant-lxc). Then (assuming the ansible playbook call within Vagrantfile is uncommented and active) run

vagrant up

It will take a bit longer because you have asked it to do much more…

so get a cup of coffee… but when you are finished your vagrant/lxc nodes will be much more usable.

Now, take a risk, destroy one of the nodes and then run vagrant up again.

vagrant status # hopefully you see your nodes running (eg node1, node2, node3)
# or sudo lxc-ls --fancy
vagrant destroy node3
# or sudo lxc-stop -n node3; sudo lxc-destroy -n node3
vagrant status # just to see that node3 has disappeared
vangrant up # yes, even though some are running and one is destroyed...
# this last command should rebuild *and provision with ansible* your lost node3
vagrant status

Have a nice day, enjoy,

-g-


comments powered by Disqus