design-systemd.rst 9.82 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234
===================
Systemd integration
===================

.. contents:: :depth: 4

This design document outlines the implementation of native systemd
support in Ganeti by providing systemd unit files. It also briefly
discusses the possibility of supporting socket activation.


Current state and shortcomings
==============================

Ganeti currently ships an example init script, compatible with Debian
(and derivatives) and RedHat/Fedora (and derivatives). The initscript
treats the whole Ganeti system as a single service wrt. starting and
stopping (but allows starting/stopping/restarting individual daemons).

The initscript is aided by ``daemon-util``, which takes care of correctly
ordering the startup/shutdown of daemons using an explicit order.

Finally, process supervision is achieved by (optionally) running
``ganeti-watcher`` via cron every 5 minutes. ``ganeti-watcher`` will - among
other things - try to start services that should be running but are not.

The example initscript currently shipped with Ganeti will work with
systemd's LSB compatibility wrappers out of the box, however there are
a number of areas where we can benefit from providing native systemd
unit files:

  - systemd is the `de-facto choice`_ of almost all major Linux
    distributions. Since it offers a stable API for service control,
    providing our own systemd unit files means that Ganeti will run
    out-of-the-box and in a predictable way in all distributions using
    systemd.

  - systemd performs constant process supervision with immediate
    service restarts and configurable back-off. Ganeti currently offers
    supervision only via ganeti-watcher, running via cron in 5-minute
    intervals and unconditionally starting missing daemons even if they
    have been manually stopped.

  - systemd offers `socket activation`_ support, which may be of
    interest for use at least with masterd, luxid and noded. Socket
    activation offers two main advantages: no explicit service
    dependencies or ordering needs to be defined as services will be
    activated when needed; and seamless restarts / upgrades are possible
    without rejecting new client connections.

  - systemd offers a number of `security features`_, primarily using
    the Linux kernel's namespace support, which may be of interest to
    better restrict daemons running as root (noded and mond).

.. _de-facto choice: https://en.wikipedia.org/wiki/Systemd#Adoption
.. _socket activation: http://0pointer.de/blog/projects/socket-activation.html
.. _security features: http://0pointer.de/blog/projects/security.html

Proposed changes
================

We propose to extend Ganeti to natively support systemd, in addition to
shipping the init-script as is. This requires the addition of systemd
unit files, as well as some changes in daemon-util and ganeti-watcher to
use ``systemctl`` on systems where Ganeti is managed by systemd.

systemd unit files
------------------

Systemd uses unit files to store information about a service, device,
mount point, or other resource it controls. Each unit file contains
exactly one unit definition, consisting of a ``Unit`` an (optional)
``Install`` section and an (optional) type-specific section (e.g.
``Service``). Unit files are dropped in pre-determined locations in the
system, where systemd is configured to read them from. Systemd allows
complete or partial overrides of the unit files, using overlay
directories. For more information, see `systemd.unit(5)`_.

.. _systemd.unit(5): http://www.freedesktop.org/software/systemd/man/systemd.unit.html

We will create one systemd `service unit`_ per daemon (masterd, noded,
mond, luxid, confd, rapi) and an additional oneshot service for
ensure-dirs (``ganeti-common.service``). All services will ``Require``
``ganeti-common.service``, which will thus run exactly once per
transaction (regardless of starting one or all daemons).

.. _service unit: http://www.freedesktop.org/software/systemd/man/systemd.service.html

All daemons will run in the foreground (already implemented by the
``-f`` flag), directly supervised by systemd, using
``Restart=on-failure`` in the respective units. Master role units will
also treat ``EXIT_NOTMASTER`` as a successful exit and not trigger
restarts. Additionally, systemd's conditional directives will be used to
avoid starting daemons when they will certainly fail (e.g. because of
missing configuration).

Apart from the individual daemon units, we will also provide three
`target units`_ as synchronization points:

  - ``ganeti-node.target``: Regular node/master candidate functionality,
    including ``ganeti-noded.service``, ``ganeti-mond.service`` and
    ``ganeti-confd.service``.

  - ``ganeti-master.target``: Master node functionality, including
    ``ganeti-masterd.service``, ``ganeti-luxid.service`` and
    ``ganeti-rapi.service``.

  - ``ganeti.target``: A "meta-target" depending on
    ``ganeti-node.target`` and ``ganti-master.target``.
    ``ganeti.target`` itself will be ``WantedBy`` ``multi-user.target``,
    so that Ganeti starts automatically on boot.

.. _target units: http://www.freedesktop.org/software/systemd/man/systemd.target.html

To allow starting/stopping/restarting the different roles, all units
will include a ``PartOf`` directive referencing their direct ancestor
target. In this way ``systemctl restart ganeti-node.target`` or ``systemctl
restart ganeti.target`` will work as expected, i.e. restart only the node
daemons or all daemons respectively.

The full dependency tree is as follows:

::

    ganeti.target
    ├─ganeti-master.target
    │ ├─ganeti-luxid.service
    │ │ └─ganeti-common.service
    │ ├─ganeti-masterd.service
    │ │ └─ganeti-common.service
    │ └─ganeti-rapi.service
    │   └─ganeti-common.service
    └─ganeti-node.target
      ├─ganeti-confd.service
      │ └─ganeti-common.service
      ├─ganeti-mond.service
      │ └─ganeti-common.service
      └─ganeti-noded.service
        └─ganeti-common.service

Installation
~~~~~~~~~~~~
The systemd unit files will be built from templates under
doc/examples/systemd, much like what is currently done for the
initscript. They will not be installed with ``make install``, but left
up to the distribution packagers to ship them at the appropriate
locations.

SysV compatibility
~~~~~~~~~~~~~~~~~~
Systemd automatically creates a service for each SysV initscript on the
system, appending ``.service`` to the initscript name, except if a
service with the given name already exists. In our case however, the
initscript's functionality is implemented by ``ganeti.target``.

Systemd provides the ability to *mask* a given service, rendering it
unusable, but in the case of SysV services this also results in
failure to use tools like ``invoke-rc.d`` or ``service``. Thus we have
to ship a ``ganeti.service`` (calling ``/bin/true``) of type
``oneshot``, that depends on ``ganeti.target`` for these tools to
continue working as expected.  ``ganeti.target`` on the other hand will
be marked as ``PartOf = ganeti.service`` for stop and restart to be
propagated to the whole service.

The ``ganeti.service`` unit will not be marked to be enabled by systemd
(i.e. will not be started at boot), but will be available for manual
invocation and only be used for compatibility purposes.

Changes to daemon-util
----------------------

``daemon-util`` is used wherever daemon control is required:

  - In the sample initscript, to start and stop all daemons.
  - In ``ganeti.backend`` to start the master daemons on master failover and
    to stop confd when leaving the cluster.
  - In ``ganeti.bootstrap``, to start the daemons on cluster initialization.
  - In ``ganeti.cli``, to control the daemon run state during certain
    operations (e.g. renew-crypto).

Currently, ``daemon-util`` uses two auxiliary tools for managing daemons
``start-stop-daemon`` and ``daemon``, in this order of preference.  In
order not to confuse systemd in its process supervision, ``daemon-util``
will have to be modified to start and stop the daemons via ``systemctl``
in preference to ``start-stop-daemon`` and ``daemon``. This
will require a basic check against run-time environment integrity:

  - Make sure that ``systemd`` runs as PID 1, which is a `simple
    check`_ against the existence of ``/run/systemd/system``.
  - Make sure ``systemd`` knows how to handle Ganeti natively. This can
    be a check against the ``LoadState`` of the ``ganeti.target`` unit.

Unless both of these checks pass, ``daemon-util`` will fall back to its
current behavior.

.. _simple check: http://www.freedesktop.org/software/systemd/man/sd_booted.html

Changes to ganeti-watcher
-------------------------

Since the daemon process supervision will be systemd's responsibility,
the watcher must detect systemd's presence and not attempt to start any
missing services. Again, systemd can be detected by the existence of
``/run/systemd/system``.

Future work
===========

Socket activation
-----------------

Systemd offers support for `socket activation`_. A daemon supporting
socket-based activation, can inherit its listening socket(s) by systemd.
This in turn means that the socket can be created and bound by systemd
during early boot and it can be used to provide implicit startup
ordering; as soon as a client connects to the listening socket, the
respective service (and all its dependencies) will be started and the
client will wait until its connection is accepted.

Also, because the socket remains bound even if the service is
restarting, new client connections will never be rejected, making
service restarts and upgrades seamless.

Socket activation support is trivial to implement (see
`sd_listen_fds(3)`_) and relies on information passed by systemd via
environment variables to the started processes.

.. _sd_listen_fds(3): http://www.freedesktop.org/software/systemd/man/sd_listen_fds.html

.. vim: set textwidth=72 :
.. Local Variables:
.. mode: rst
.. fill-column: 72
.. End: