📜 ⬆️ ⬇️

Systemd: write your own .service and .target

I got Linux on my home computer, and I hurried to get used to the new OS. It was installed with the systemd init process. This was my first acquaintance with this new tool. I use my laptop for everyday life and for programming. I wanted to include work programs (Apache2 and MySQL) only for a while, while I use them, so as not to waste my computer resources. Additionally, for testing, I wrote a bash script that dumps the contents of one of the MySQL databases from the hard disk into RAM (in tmpfs) - so the tests are performed much faster. In theory, I could start my work day like this:
systemctl start apache2.service systemctl start mysqld.service /root/scripts/mysqld-tmpfs start 

And finish it:
 systemctl stop apache2.service systemctl stop mysqld.service /root/scripts/mysqld-tmpfs stop 

But I wanted to do things “right”.

What did I want?


I wanted to achieve 2 goals:
  1. I was too lazy to write 2 commands (running apache and starting mysql), because I knew that both programs would always be turned off and on synchronously. I wanted to perform this operation as a team.
  2. The case smacked of trouble, if the computer restarts while my database is sitting in tmpfs - all files will be lost. Of course, I made backups, but again I was too lazy to restore them manually after each unexpected reboot.


What I've done?


In the end, I combined Apache2 and MySQL into one target. This allowed both services to be started with the same command. And I declared my mysqld-tmpfs script as a service in the eyes of systemd. Being a service, I am sure that systemd will correctly stop it if the system goes to reboot or some other abnormal situation, and my database will be saved to the hard disk without loss.

What is a service?


This is some kind of program that runs in the background and provides useful functionality. For example, Apache web server. Services can be started and stopped. Some services can start and stop automatically on certain events (OS loading, OS unloading, etc.). They can also be started / stopped manually. The service is declared in the /etc/systemd/system/my-name.service files (with the suffix “.service”).
')

What is target?


Target in systemd is very similar to runlevel in openRC, but it’s still different things. First, the target allows you to group 1 or more services into a single unit. By grouping services into targets, they are easier to manage. Secondly, systemd automatically turns on / off targets for events. “Turning on” the target means turning on all the services that it combines in itself. For example, if systemd has target configured as default my-favorite.target, then when systemd to boot, systemd will turn on all services that are declared inside my-favorite.target. At some point in the console, you can type:
 systemctl isolate my-another.target 

All services from my-another.target will be enabled, and all enabled services not from my-another.target will be disabled. This is very similar to the runRC switching in openRC. However, systemd supports the inclusion of more than 1 target. Here is an example:
 #   my-favorite.target      systemctl isolate my-favorite.target #      targets   1 target systemctl start my-another.target 

After these commands are executed, the system will combine the services from my-favorite.target and my-another.target.

How did I do it?


In the end, I got this mysqld-tmpfs.service file:
 Description=Mount a MySQL database into tmpfs. #  /root/scripts/tmpfs      ,     mysql .    mysql    ,  ,      : #Requires=mysqld.service #After=mysqld.service [Service] #   systemd,        1 . Man page    . Type=oneshot #      . ExecStart=/root/scripts/mysqld-tmpfs start #      . ExecStop=/root/scripts/mysqld-tmpfs stop #   systemd,     ,       .   ,   :          ,    , ..    . RemainAfterExit=yes 

And here is such a programming.target file:
 [Unit] Description=Working/Programming target Requires=mysqld.service Requires=apache2.service #       “Requires=another.service”,       . 


What were the problems?


When stopping programming.target for some reason, the underlying apache2.service and mysqld.service did not stop. Having read the man page properly, I found the problem: systemd stops the services “lazily” - if no one requires the started service and it was not started explicitly, but as a dependency for some other service, systemd will stop it only with one of the 3 circumstances:
  1. Some other service will start, which in its declaration indicates that it conflicts with our service.
  2. Run systemctl isolate some-another.target or systemctl stop this.service.
  3. Our service can request in its declaration to stop itself not in a lazy way, but in an active way, adding the following section to the [Unit] section: StopWhenUnneeded = true


Declarations of “foreign” services can be changed by creating files /etc/systemd/system/name-i-alter.service.d/*.conf. I just created /etc/systemd/system/apache2.service/auto-stop.conf and /etc/systemd/system/mysqld.service.d/auto-stop.conf and put that line there.

Another problem that I stumbled upon was that systemd does not like symlinks very much. I'm not a big fan of “messing up” system directories like / etc, / bin, / usr with my local waste products, so initially I tried my /etc/systemd/system/mysqld-tmpfs.service to symlink to / root / scripts / mysqld- tmpfs.service file i. store the file itself in the user's home root directory. But the systemctl team refused to work with such a service giving out obscure errors. It turned out that the systemd makes a certain part of its internal kitchen on symlinks, and then it is “difficult” for it to distinguish the internal kitchen (its symlinks) from third-party * .service files (if they are symlinks too). Removing symlink from /etc/systemd/system/mysqld-tmpfs.service and copying the contents of this file there, I solved this problem. A more detailed description of this problem can be found here: bugzilla.redhat.com/show_bug.cgi?id=955379

Result


I achieved my goal. Starting a working day:
 systemctl start programming.target 

When you need to run tests on your project:
 systemctl start mysqld-tmpfs.service 

When I want to remove the database from tmpfs to the hard disk (although in practice I almost don’t do that, I just leave the database to tmpfs for a whole day, and when I turn off, systemd starts dismantling from tmpfs to my hard disk):
 systemctl stop mysqld-tmpfs.service 

When I finished work and I want to stop working programs:
 systemctl stop programming.target 


Cheat sheet


Some useful things when working with systemd:


I hope this article will help someone with the mastering systemd. I tried to make it compact, and if I missed any additional questions from attention, ask in the comments!

Source: https://habr.com/ru/post/275645/


All Articles