Creating The Perfect Software Development Environment: Day 09

An Easier Way Of Installing Development Tools

Last time we used Ansible to handle the chore of installing the software we wanted into our development Vagrant box. While this is a perfectly acceptable solution, it does have the drawback of forcing you to understand all the steps required to properly install something. For more complex configurations you have not only install the software but you also have to tweak a configuration file or two and maybe even open a port in a firewall. Wouldn't be nice if somebody else already figured all of that out for you and you could just use their work? That is exactly what Ansible Roles are. Authoring Roles requires a fair amount of Ansible knowledge, so we won't be discussing how to do that, but using them is simple.

Ansible Galaxy

Galaxy is your hub for finding, reusing and sharing Ansible content.

Galaxy feels much like GitHub. People upload work that is useful to them and, if you are lucky, it is useful to you as well. For this discussion, we'll be installing MySQL via the geerlingguy.mysql Role, selected because it appears on the most starred list and it has been consistently updated.

Tool Installation via Ansible Roles

From a Packer perspective, all we have to do to use Roles is to add a single line to our Packer descriptor and modify the playbook a bit.


    "provisioners": [
        {
            "type"            : "shell",
            "environment_vars": ["DEBIAN_FRONTEND=noninteractive"],
            "script"          : "scripts/install-ansible.sh"
        },
        {
            "type"            : "ansible-local",
            "playbook_file"   : "ansible/playbook.yml",
            "galaxy_file"     : "ansible/requirements.yml",
            "extra_arguments" : ["--verbose"]
        },
        {
            "type"            : "shell",
            "environment_vars": ["DEBIAN_FRONTEND=noninteractive"],
            "scripts"         : ["scripts/prepare-for-compression.sh"]
        }
    ],     

All we did was add a value for the galaxy_file attribute, which tells Ansible which roles you intend on using in your playbook. Since we are using a single role, we only have one line in the requriements.yml file.


- src: geerlingguy.mysql
            

To make use of the role, we have to augment the playbook, having it load the role and providing any required configuration values. Many roles have reasonable defaults so you might not have to provide any overrides at all. Check your role's documentation on how to customize its invocation.


---
- name: Install Tools
  hosts: localhost
  become: true
  tasks:
      - name: Favorite Tools
        apt:
            name        : "{{ item }}"
            state       : present
            update_cache: yes
        with_items:
            - "tree"
            - "ntpdate"
            - "grsync"

- name: Install MySQL
  hosts: localhost
  become: true
  tasks:
      - name: Invoke The Role
        import_role:
            name: geerlingguy.mysql
        vars:
            mysql_user_home: /root
            mysql_user_name: root
            mysql_user_password: root
            

Conclusion

Today we learned how Ansible Roles can reduce the amount of work needed to install a piece of software. The trade-off of using somebody else's role is that you cede precise control over how things are configured and laid out. To regain that control, you can write your own roles and configure things the way you like. You can then share the role between teams within your organization, ensuring that environments are configured similarly.

Full Packer File


{
    "description": "Builds a Xubuntu 18.04 desktop box with various software development tools installed",
    "min_packer_version": "1.2.3",

    "variables": {
        "ssh_name"            : "vagrant",
        "ssh_pass"            : "vagrant",
        "virtualbox_appliance": "bionic-beaver.ova",
        "comment"             : "Test of Vagrant Cloud upload.",
        "cloud_user"          : "{{env `VAGRANT_CLOUD_ACCOUNT`}}",
        "cloud_token"         : "{{env `VAGRANT_CLOUD_TOKEN`}}"
    },

    "builders": [{
        "type"        : "virtualbox-ovf",
        "source_path" : "{{user `virtualbox_appliance`}}",
        "ssh_username": "{{user `ssh_name`}}",
        "ssh_password": "{{user `ssh_pass`}}",

        "boot_wait"           : "30s",
        "format"              : "ova",
        "guest_additions_mode": "disable",
        "headless"            : false,
        "keep_registered"     : false,
        "shutdown_command"    : "sudo shutdown --poweroff now",
        "shutdown_timeout"    : "2m",
        "skip_export"         : false,
        "output_directory"    : "output-virtualbox-ovf",
        "vboxmanage": [
		        ["modifyvm", "{{.Name}}", "--vram", "32"],
		        ["modifyvm", "{{.Name}}", "--memory", "2048"],
		        ["modifyvm", "{{.Name}}", "--cpus", "1"]
	      ],
        "vm_name"             : "packer-bionic-xubuntu"
    }],
    "provisioners": [
        {
            "type"            : "shell",
            "environment_vars": ["DEBIAN_FRONTEND=noninteractive"],
            "script"          : "scripts/install-ansible.sh"
        },
        {
            "type"            : "ansible-local",
            "playbook_file"   : "ansible/playbook.yml",
            "galaxy_file"     : "ansible/requirements.yml",
            "extra_arguments" : ["--verbose"]
        },
        {
            "type"            : "shell",
            "environment_vars": ["DEBIAN_FRONTEND=noninteractive"],
            "scripts"         : ["scripts/prepare-for-compression.sh"]
        }
    ],
    "post-processors": [
        [
            {
                "compression_level"  : 9,
                "keep_input_artifact": false,
                "output"             : "vagrant/bionic-xubuntu.box",
                "type"               : "vagrant"
            },
            {
                 "access_token"       : "{{user `cloud_token`}}",
                 "box_tag"            : "{{user `cloud_user`}}/bionic-xubuntu",
                 "type"               : "vagrant-cloud",
                 "version"            : "{{isotime \"2006.01.0203\"}}",
                 "version_description": "{{user `comment`}}"
            }
        ]
    ]
}