Skip to content
Ansible role for creating Systemd services for docker containers
Branch: master
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
defaults
handlers
meta
tasks
templates
tests
vars
.gitlint
.travis.yml
LICENSE
README.md

README.md

Docker role mhutter.docker-systemd-service

Build Status

Generic role for creating systemd services to manage docker containers.

Example

- name: Start WebApp
  include_role:
    name: mhutter.docker-systemd-service
  vars:
    container_name: myapp
    container_image: myapp:latest
    container_links: [ 'mysql' ]
    container_volumes:
      - '/data/uploads:/data/uploads'
    container_ports:
      - '3000:3000'
    container_env:
      MYSQL_ROOT_PASSWORD: "{{ mysql_root_pw }}"
    container_labels:
      - 'traefik.enable=true'

This will create:

  • A file containing the env vars (either /etc/sysconfig/mysql or /etc/default/mysql).
  • A systemd unit which starts and stops the container. The unit will be called <name>_container.service to avoid name clashes.

Role variables

  • container_name (required) - name of the container

Docker container specifics

  • container_image (required) - Docker image the service uses
  • container_args - arbitrary list of arguments to the docker run command
  • container_cmd - optional command to the container run command (the part after the image name)
  • container_env - key/value pairs of ENV vars that need to be present
  • container_volumes (default: []) - List of -v arguments
  • container_host_network (default: false) - Whether the host network should be used
  • container_ports (default: []) - List of -p arguments
  • container_link (default: []) - List of --link arguments
  • container_labels (default: []) - List of -l arguments
  • container_docker_pull (default: yes) - whether the docker image should be pulled
  • container_cap_add (default []) - List of capabilities to add
  • container_cap_drop (default {}) - List of capabilities to drop

Systemd service specifics

  • enabled (default: yes) - whether the service should be enabled
  • masked (default: no) - whether the service should be masked
  • state (default: started) - state the service should be in - set to absent to remove the service.

Installation

This role requires the docker python module. Install it with pip3 install docker or apt install python3-docker (or drop the 3 for python 2.x).

Put this in your requirements.yml:

- role: mhutter.docker-systemd-service

and run ansible-galaxy install -r requirements.yml.

Gotchas

  • When the unit or env file is changed, systemd gets reloaded but existing containers are NOT restarted.
  • Make sure to quote values for container_ports, container_volumes and so on, especially if they contain colons (:). Otherwise YAML will interpret them as hashes/maps and ansible will throw up.

About orchestrating Docker containers using systemd.

The concept behind this is to define systemd units for every docker container. This has some benefits:

  • systemd is a well-known interface
  • all services are controllable via the same tool (systemctl)
  • all logs are accessible via the same tool (journalctl)
  • dependencies can be defined
  • startup behaviour can be defined
  • by correctly defining the unit (see below), we can ensure we always have a clean container.

Here is an example myapp_container.service unit file (about what's produced by above code):

[Unit]
# define dependencies
After=docker.service
PartOf=docker.service
Requires=docker.service

[Service]
# Load ENV vars from a file. Note that this env vars will only be
# accessible in the context of the Exec* commands, and not within the
# container itself. To make env-vars accessible within the Container, we use
# the `--env-file` flag for the `docker run` command.
EnvironmentFile=/etc/sysconfig/myapp

# Even though we explicitly run the container using the `--rm` flag, there
# may be leftover containers (eg. after a system-, docker- or app-crash).
# Starting a container with an existing name will always fail.

ExecStartPre=-/usr/bin/docker rm -f myapp

# actually run the container.
# `--name` to identify the container
# `--rm` ensure the container is removed after stopping
# `--env-file` make ENV vars accessible to app
# `--link mysql` link to a container named `mysql`. The DB will then be
#                accesible at `mysql:3306`
# `-v` mount `/data/uploads` into the container
# `-p 3000:3000` expose port 3000 on the network
ExecStart=/usr/bin/docker run --name myapp --rm --env-file /etc/sysconfig/myapp --link mysql -v /data/uploads:/data/uploads -p 3000:3000 registry.cust.net/myapp/myapp:latest
# note that there is no `--restart` parameter. This is because restarting
# is taken care of by `systemd`.

# Stop command.
ExecStop=/usr/bin/docker stop myapp

# Ensure log messages are correctly tagged in the system log.
SyslogIdentifier=myapp

# Auto-Restart the container after a crash.
Restart=always


[Install]
# make sure service is started after docker is up
WantedBy=docker.service
You can’t perform that action at this time.