Ansible and Docker

Automate you docker containers with ansible

If you’re like me and you have a home lab you are constantly changing things, whether that is changing the way your network works or adding new hardware. For me that meant every time I got a new server I had to manually configure things. Some of it was easier than others, however a constant pain has been moving my docker containers from host to host without using any kind of orchestration tools like Kubernetes. Enter Ansible. Anisble is a configuration management tool that helps lots of Sysadmin and engineers to manage fleets of servers reliably. I use Ansible everyday in my day job to deploy all sorts of things to the very large amount of servers that I’m responsible for, but at home I still “handjam” everything. But after months ( maybe years) of resisting – who wants to come home to do more work – I’ve finally decided to manage the servers at home the way I manage them at work. The first project I decided to tackle was to automate some simple tasks, the first one being installing docker. So I created an extremely simple play that just that.
---
- name: install pre-reqs
  apt:
    name: "{{item}}"
    state: present
  with_items:
    - "ca-certificates"
    - "curl"
    - "software-properties-common"

  

- name: docker repo key
  apt_key:
    url: https://download.docker.com/linux/ubuntu/gpg
    state: present

- name: docker repo install
  apt_repository:
    repo: deb [arch=amd64] https://download.docker.com/linux/ubuntu bionic stable
    state: present
    

- name: install docker
  apt: 
   name: docker-ce
   state: present
   update_cache: yes

- name: start service
  service:
    name: docker
    state: started
    enabled: true

- name: ensure docker-py is installed
  apt:
    name: "{{ item }}"
    state: present
  with_items:
    - "python-docker"
    - "python3-docker"

- name: add user to docker group
  user:
    name: ryan
    group: docker
    append: yes
With just those few lines of YAML, I’m able to install the pre-reqs, set up the docker repository, install docker, and add myself to the docker group. Next I decided to tackle mounting my nfs mounts that my Plex server uses to serve my movies.
---
- name:
  file:
      path: "{{ item }}"
      state: directory
  with_items:
    - ["/config", "/key", "/music", "/movies", "/pics", "/vids", "/tv" , "/weddingg_vids", ]
---
- name: Mount config  nfs 
  mount:
    path: /config
    src:  freenas:/mnt/tank/docker_host
    fstype: nfs
    state: mounted
- name: Mount key  nfs 
  mount:
    path: /key
    src:  freenas:/mnt/tank/key
    fstype: nfs
    state: mounted

- name: Mount music  nfs 
  mount:
    path: /music
    src:  freenas:/mnt/tank/music
    fstype: nfs
    state: mounted
This creates the directories that my nfs shares backed by Freenas would be mounted to. One of the import mount points is /config. This is the directory that does the magic for my docker containers that save state. As long as I mount this share and bind the correct path to the containers my container configs/databases will always be the same regardless of how many times I rebuild my docker hosts With all of that working I can now use Ansible to spin up my containers
---
- name: install jwilder-nginx
  docker_container:
    name: nginx-proxy
    image: jwilder/nginx-proxy:latest
    volumes:
     -  /var/run/docker.sock:/tmp/docker.sock:ro
    exposed_ports:
        - "80:80"
    restart_policy: always
And that’s it, I can keep writing plays similar to the one above and my containers will be created and started automatically with all the correct options. And because Ansible is idempotent I never have to worry about stepping on any running containers, I simply modify my play, run Ansible and off to the races. One thing to note is that container listed above is excellent . It allows you to declare a wild card DNS A record that points to the container host. Any container with the following env variables, VIRTUAL_HOST and VIRTUAL_PORT are automatically proxied behind the scenes to the mapped URL. For example 192.168.29:9000 can become portainer.foo.bar without having to add more DNS records and manually setting up a reverse proxy.