In this tutorial we will learn how create a service file to run a script as a service using systemd, and write an ansible role to automate the process.

Hint This tutorial assumes you are familiar with ansible.

Systemd Introduction and Service files

Here we will introduce you only to the very basics of systemd, to learn more about systemd follow the lnks bellow.

Systemd is a suite of basic building blocks for a Linux system, it runs as PID 1 and is used to bootstrap all other services after boot and manage the life cycle of Linux services.

Systemd services files can be found in one of these two locations /lib/systemd/system or /etc/systemd/system where the second location takes precedence over the first one.

You can override a systemd service file by creating a directory called after the service name and ends with .d e.g: if a service is called test.service the directory must be called test.service.d, inside this directory we put files that end with .conf and contain options that override the same options defined in the service file.

The basic structure of a service file is as follows:

[Unit]
Description=A test unit
[Service]
ExecStart=/usr/local/bin/test
Type=simple
StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=test

The previous file in an INI file and contains the very basic structure of a systemd unit file.

The first section [Unit] defines general information about the systemd unit. The Description option describes the unit in general using few words.

The [Service] section defines the service it self and is very important for the service operation.

ExecStart defines the absolute path to the binary ised to run the service. Type defines the type of the service, here we care about only two types, simple which means that the script’s execution binary will run in foreground, the other type is forking which means the script’s execution binary will fork in background and probably write the PID of the child process to a file that must be identified using the PIDFile option to enable systemd to control the service.

StandardOutput and StandardError defines where to send the program’s standard output and error here it is sent to syslog.

The SyslogIdentifier is used to identify the service in syslog files, this will be used later to direct output to a separate file for the service.

When this file is saved as test.service in /etc/systemd/system and started using this command sudo systemctl start test.service the script found at /usr/local/bin/test is executed as a background process and can be stopped using sudo systemctl stop test.service, we can query its status using sudo systemctl status test.service.

For more information about systemd check this tutorial on digitalocean also check the systemd site here.

Send syslog output to a separate file

In the previous section we sent the output of our service to syslog which writes to a the log file /var/log/syslog and writes the value of SyslogIdentifier to the log line to identify that this output line came from this service.

We can tell syslog daemon to send the output from this service to a different file by creating a new file called test.conf in this directory /etc/rsyslog.d/ and put the following line in it.

if $programname == 'test' then /var/log/test.log

Here the programname test is taken from SyslogIdentifier which is defined in the service file.

Restart syslog for changes to take effect

sudo systemctl restart syslog

Now the ouyput of the service called test is redirected to /var/log/test.log.

Use an ansible role to automate the previous tasks

Now we will use ansible to create a role that creates some service files, starts them and possibly send their output to different files using syslog.

We will use variables to define the services we want to create and the files we want to redirect logs to them.

Here is a sample ansible playbook to install the previous service on a server, setup syslog for it and start it.

---
  - hosts: server1
    become: true
    gather_facts: true

    vars:
      - systemd_services:
        - src: test
          dst: /usr/local/bin/test
          name: test
          upload: true
          type: simple
          description: test service
          log_file: /var/log/test.log
          started: True
        - dst: /usr/local/bin/dst
          name: dst
          type: forking
          pid_file: /var/run/dst.pid


    roles:
      - moshensy.systemd_service

In the previous playbook we are deploying the test service to a server called server1.

We used the systemd_services variable, which is an array of dictionaries and each one defines a service, the service has the following options

src defines the location of the program file on the machine which is running ansible.

dst defines where the program will be saved on the server.

upload is a boolean variable which specifies if the file will be uploaded to the server.

name defines the name of the service.

type defines the service’s type either simple or forking, if the type of the service is forking you must define the pid_file option to tell systemd about the file that will contain the PID of the child process to monitor.

description defines the service’s description.

log_file defines the log file to be used for sending standard output and error to it.

started is a boolean which specifies if the service must be started or not.

Conclusion

In this tutorial we learned how to create a service file in systemd, send its output to syslog and then use syslog to send each service’s output to a different file.

Finally we learned how to use an ansible role to automate all of the previous tasks.

I hope you enjoyed it, any feedback will be highly appreciated you can use the comment section below or the ChatBot or email me directly at mohsen47@hotmail.co.uk.