There I certain services, that I typically start together: say zookeeper+kafka+elassandra. Is there a way how to write composite unit: all starts together, all dies together? Meaning if I start this, it will delegate to start all that, and vice versa. What would be proper way of doing that?

2 Answers
2

One way is to have all three services depend on the remaining two using Requires=.

one.service: Requires=two.service three.service

two.service: Requires=one.service three.service

three.service: etc.

This won't create a loop – dependencies are independent from startup ordering.

(That said, you should declare some Before= or After=, e.g. if kafka needs to run after zookeeper.)

The other method is to create a .target unit, have it depend on your three services, and the services be PartOf= the unit. (Unfortunately it is not yet possible to have ConsistsOf= in the .target itself.)

all.target: Requires=one.service two.service three.service

one.service: PartOf=all.target

etc.

(Again, you should additionally declare dependencies and ordering between the services; don't rely only on the .target starting everything.)

thank you very much, I like in first approach, that I can start any of these services and all will come up in correct order. And second will come in handy for multi-starting services, which need not to be started always together. Thanks for both, I will use them — both.
– Martin MuchaOct 23 '18 at 19:04

The simplest one (which does some of what uou describe, but not all) is to create a target unit and add dependencies on your service units (for example, Requires=zookeeper.service kafka.service elassandra.service and also set After= to the same units.) A target unit is helpful in starting all these units together, but it doesn't really help you stop them all together (using systemctl stop on the target unit won't stop its dependencies.) There are ways you can stop units, for example systemctl isolate multi-user.target will stop all units that are not dependencies of that target, which means units started manually will be stopped, but this is much stronger than stopping a small set of units, so probably not a great fit...

A better approach is perhaps using the PartOf= directive, which does exactly as you describe. You can either create a "dummy" service unit to manage all services together, or pick one of your services and make the others PartOf that one service.

You need to configure PartOf= in all the units you want to start and stop together, in your case, zookeeper.service, kafka.service and elassandra.service. But please note you don't necessarily need to modify the service unit files themselves (for example, if they're shipped with the software itself in deb or rpm packages.) You can use override files (which you can create using systemctl edit) to add a small configuration snippet to an existing unit, which should make it easy for you to define PartOf= relationships between units even if they're defined in files you would prefer not to modify.