Ansible

Generate systemd files to run Docker containers

Thomas Krag

Technical Lead Infrastructure at Wehkamp

# extrainfo.json
{

    "Heritage": "Danish",
    "I care about": [
        "devops",
        "automation",
        "engineering and company culture",
        "beer"
    ],
    "Twitter": "@ildiroen",
    "Slides": "https://slides.com/thomaskrag/ansible-docker-systemd"

}

Wehkamp

Legacy

In-house datacenter

VMWare

Microsoft Server

.NET

MSSQL

Monolith

Blaze

Amazon Web Services

Mesos/Marathon

Docker

Scala/node.js

Cassandra

Microservices

Blaze Environment Stack

Mesos/Marathon

Scala/node.js

EC2

AWS

Cassandra

ElasticSearch

(simplified)

What do we use Ansible for?

Mesos/Marathon

Scala/node.js

EC2

AWS

Cassandra

ElasticSearch

Why systemd for Docker?

Why not Docker module for Ansible?

  • We lose the benefits of systemd
  • We can't define dependencies

An idea is born

Using Jinja2 templates with Ansible

Bas Tichelaar

What services?

  • consul
  • dnsmasq
  • mesos
  • prometheus
  • haproxy
  • zookeeper
  • kibana
  • grafana
  • fluentd
  • docker-gc
  • and more...
[Service]
Restart=always
RestartSec=10
TimeoutStartSec=0
ExecStartPre=-/usr/bin/docker kill {{ name }}
ExecStartPre=-/usr/bin/docker rm {{ name }}
ExecStartPre=/usr/bin/docker pull {{ image }}
{%if execstartpost is defined %}{% for command in execstartpost %}ExecStartPost={{ command }}{% endfor %}{% endif %}

ExecStart=/usr/bin/docker run \
    --name {{ name }} \
{% if team is defined %}
    --label team={{ team }} \
{% else %}
    --label team=purple \
{% endif %}
    --label container_type=infrastructure \
{% for option, value in docker_opts.iteritems() %}
{% if value is string %}
    --{{ option }} "{{ value }}" \
{% elif value == true %}
    --{{ option }} \
{% elif value is mapping %}
{% for key, item in value.iteritems() %}
    --{{ option }} "{{ key }}={{ item }}" \
{% endfor %}
{% else %}
{% for item in value %}
    --{{ option }} "{{ item }}" \
{% endfor %}
{% endif %}
{% endfor %}
    {{ image }} {% if command is defined %}{{ command }}{% endif %}

{% if execstop is defined %}{% for command in execstop %}ExecStop=-{{ command }}{% endfor %}{% endif %}

ExecStop=/usr/bin/docker stop -t 60 {{ name }}

[Install]
WantedBy=multi-user.target
[Unit]
Description={{ name }} container
Requires=docker.service {% if requires is defined %}{% for item in requires %}{{ item }}.service {% endfor %}{% endif %}

{% if before is defined %}Before={% for item in before %}{{ item }}.service {% endfor %}{% endif %}

After=docker.service {% if after is defined %}{% for item in after %}{{ item }}.service {% endfor %}{% endif %}

{% if binds_to is defined %}BindsTo={% for item in binds_to %}{{ item }}.service {% endfor %}{% endif %}

{% if wants is defined %}Wants={% for item in wants %}{{ item }}.service {% endfor %}{% endif %}
---
- name: Run the Blaze-router container for .nl
  include: ../../docker-systemd/tasks/main.yml
  vars:
    execstop:
      - "/usr/local/bin/init_elb.sh deregister"
    execstartpost:
      - "/usr/local/bin/init_elb.sh register"
    after:
      - consul
      - fluentd
    name: blaze-router
    image: "{{ container_router_nl }}"
    docker_opts:
      publish:
        - "{{ inventory_hostname }}:80:80"
        - "{{ inventory_hostname }}:81:81"
        - "{{ inventory_hostname }}:443:443"
        - "{{ inventory_hostname }}:9102:9101"
      net: bridge
      volume:
        - /etc/ssl/certs:/etc/ssl/certs:ro
      env:
        SERVICE_443_NAME: blaze-router
        SERVICE_81_CHECK_HTTP: /status
        SERVICE_9101_NAME: prometheus-router-exporter
        SERVICE_9101_TAGS: exposes-metrics
      log-driver: fluentd
      log-opt:
        fluentd-tag: docker.blaze-router
  tags:
    - dockeronly
    - haproxy
  when: blaze_country == "nl"

But does it work?

Moment of truth!

ansible-docker-systemd

By Thomas Krag

ansible-docker-systemd

This presentation shows how to leverage Ansible templating (jinja2) to create systemd files to manage Docker containers.

  • 531