Generic role for creating systemd services to manage docker containers.
- 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.
container_name
(required) - name of the container
container_image
(required) - Docker image the service usescontainer_args
- arbitrary list of arguments to thedocker run
commandcontainer_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 presentcontainer_volumes
(default: []) - List of-v
argumentscontainer_ports
(default: []) - List of-p
argumentscontainer_link
(default: []) - List of--link
argumentscontainer_labels
(default: []) - List of-l
argumentscontainer_docker_pull
(default: yes) - whether the docker image should be pulled
enabled
(default: yes) - whether the service should be enabledmasked
(default: no) - whether the service should be maskedstate
(default: started) - state the service should be in - set toabsent
to remove the service.
Put this in your requirements.yml
:
- role: mhutter.docker-systemd-service
and run ansible-galaxy install -r requirements.yml
.
- 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.
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