Set up digital ocean block storage with Ansible

Ansible is a great tool for automating environment setup and deploy. This article shows how to programmatically set up digital ocean block storage with Ansible. I started by reading this article on how to use the digital ocean manually and built an Ansible runbook inspired by it. The complete runbook is available as a gist, we will build it piece by piece in this post.

Digital Ocean block storage

Digital ocean lets you create volumes and attach them to droplets. For those of you more familiar with Amazon Web Services, digital oceans volumes are to droplets what EBS volumes are to EC2 instances.

/assets/ebs-dovol.svg

When you create a volume in digital ocean and attach it to a droplet it shows up in the /dev/disk-by-id folder. It is not formatted into partitions and does not have a file system.

Goals

The goal of our runbook is to automate the setup of a new volume attached to a droplet so that it can be used by applications. I use this runbook in my chatbot before installing a Redis datastore that uses the volume. The runbook should also be a noop with droplets that are already set up

Finding the name of the volume

Let's start by defining the first step to get the volume name. We look at what's in /dev/disk/by-id and discard entries that contain part (they refer to partitions of disks instead of the whole volume). We store the name of the volume for the future steps:

- name: Get the volume name
  shell: ls /dev/disk/by-id/ | grep -v part
  register: volume_name_raw

- set_fact:
    volume_name: "{{ volume_name_raw.stdout }}"

Now we want to be able to run this recipe multiple times, so let's find a way to know if the disk is already set up. We can do that by checking /etc/fstab which describes the disk to mount on boot:

- name: Check if the volume is already setup
  command: grep '{{ volume_name }}' /etc/fstab -q
  register: volume_present
  ignore_errors: True

Formatting the volume

All the following steps setup the volume as ext4 and should not run if the volume is already setup:

- name: Label the volume
  command: parted /dev/disk/by-id/{{ volume_name }} mklabel gpt
  when: volume_present|failed

- name: Create an ext4 partition
  command: parted -a opt /dev/disk/by-id/{{ volume_name }} mkpart primary ext4 0% 100%
  when: volume_present|failed

- name: Build the ext4 metadata
  command: mkfs.ext4 /dev/disk/by-id/{{ volume_name }}-part1
  when: volume_present|failed

- name: Create the mount point
  command: mkdir -p /mnt/data
  when: volume_present|failed

Mounting the volume and persisting the mount

Finally, the volume is ready, we want to ensure that it is mounted and configured in fstab. This will be a noop for a volume that is already mounted and there:

- name: Mount volume read-write
  mount:
    path: /mnt/data
    src: /dev/disk/by-id/{{ volume_name }}-part1
    fstype: ext4
    opts: defaults,discard
    state: mounted

As I wrote previously, you can then use the volume for various applications. I use them to persist a Redis database for my bot. It is really handy to have an ansible cookbook to automate their set up. The complete runbook with all the steps is available as a gist.

Don't forget to also check out the articles discussing how I build my chatbot using Digital Ocean: Syncing org mode reminders to my bot and Managing my todos, notes, and reminders.