Services and automating tasks
Lab Setup
- We will be using a virtual machine in the faculty's cloud.
- When creating a virtual machine in the Launch Instance window:
- Name your VM using the following convention:
scgc_lab<no>_<username>
, where<no>
is the lab number and<username>
is your institutional account. - Select Boot from image in Instance Boot Source section
- Select SCGC Template in Image Name section
- Select the m1.medium flavor.
- Name your VM using the following convention:
- The username for connecting to the VM is
student
.
systemd services
Most common Linux distribution now ship with the systemd software suite to manage a services and other system components. The use of systemd provides a uniform configuration interface across multiple Linux distributions, and allows configuring service prerequisites and various environment parameters.
We can inspect the status of a service using systemctl status servicename
.
$ systemctl status ssh
● ssh.service - OpenBSD Secure Shell server
Loaded: loaded (/lib/systemd/system/ssh.service; enabled; vendor preset: enabled)
Active: active (running) since Mon 20XX-02-30 00:00:00 UTC; 33min ago
Docs: man:sshd(8)
man:sshd_config(5)
Process: 674 ExecStartPre=/usr/sbin/sshd -t (code=exited, status=0/SUCCESS)
Main PID: 677 (sshd)
Tasks: 1 (limit: 1696)
Memory: 4.9M
CGroup: /system.slice/ssh.service
└─677 sshd: /usr/sbin/sshd -D [listener] 0 of 10-100 startups
Feb 30 00:00:00 scgc-services systemd[1]: Starting OpenBSD Secure Shell server...
Feb 30 00:00:00 scgc-services sshd[677]: Server listening on 0.0.0.0 port 22.
Feb 30 00:00:00 scgc-services sshd[677]: Server listening on :: port 22.
Feb 30 00:00:00 scgc-services systemd[1]: Started OpenBSD Secure Shell server.
In the service's status command's output we can see a number of useful stats about the service:
- whether the service is enabled (i.e., it is automatically started when the
system boots) - the
enabled;
parameter in the line starting withLoaded
; - the time the service has started and how long it has been running;
- what processes and how many are in the service's control group;
- the last few lines output by the service to its output or standard error.
Changing the state of services
You can start and stop a service using systemctl start servicename
and
systemctl stop servicename
, respectively.
Try stopping the ssh
service. What do you observe? Try connecting to the
virtual machine again. What happens if you reboot the virtual machine?
Services can be enabled or disabled using systemctl enable servicename
and
systemctl disable servicename
.
Install the nginx
service using apt
and observe its status. Toggle its
enabled state (if disabled, enable it; if enabled, disable it). How does the
toggling affect its current running state?
Be careful when disabling services. Some services, like ssh
are critical. If
disabled completely, you may become unable to connect to the system even if you
reboot it.
Look for enable UNIT in man systemctl
to find out how you can immediately
affect the service's running state when enabling or disabling it.
Tuning service files
systemd services are configured using files under /lib/systemd/system
or
/etc/systemd/system
. We can use systemctl cat servicename
to see the
configuration of a particular systemd unit.
$ sudo systemctl cat nginx
# /lib/systemd/system/nginx.service
# Stop dance for nginx
# =======================
#
# ExecStop sends SIGSTOP (graceful stop) to the nginx process.
# If, after 5s (--retry QUIT/5) nginx is still running, systemd takes control
# and sends SIGTERM (fast shutdown) to the main process.
# After another 5s (TimeoutStopSec=5), and if nginx is alive, systemd sends
# SIGKILL to all the remaining processes in the process group (KillMode=mixed).
#
# nginx signals reference doc:
# http://nginx.org/en/docs/control.html
#
[Unit]
Description=A high performance web server and a reverse proxy server
Documentation=man:nginx(8)
After=network.target
[Service]
Type=forking
PIDFile=/run/nginx.pid
ExecStartPre=/usr/sbin/nginx -t -q -g 'daemon on; master_process on;'
ExecStart=/usr/sbin/nginx -g 'daemon on; master_process on;'
ExecReload=/usr/sbin/nginx -g 'daemon on; master_process on;' -s reload
ExecStop=-/sbin/start-stop-daemon --quiet --stop --retry QUIT/5 --pidfile /run/nginx.pid
TimeoutStopSec=5
KillMode=mixed
[Install]
WantedBy=multi-user.target
The service configuration file contains various parameters that define generic
service information (e.g., the Description and Documentation fields in the
[Unit]
section), dependencies on other services (e.g., the After and
WantedBy fields in the [Unit]
and [Install]
sections), as well as
service-specific parameters (e.g., where the service's PID should be stored, how
systemd should keep track of processes, execution steps, and so on).
systemd can configure a number of parameters for the process. For example, the
maximum number of open file can be configured by setting the LimitNOFILE (see
man systemd.exec
for more details) parameter in the [Service]
section of the
process.
Use systemctl edit nginx
to edit nginx's configuration parameters and set the
maximum number of open files to a really low number (e.g., 5).
You can define just the section name and the parameters you want to override; you don't need to copy all the configuration in the original service file.
After configuring the parameter, you should see the following when running
systemctl cat nginx
(note that an overrides file has been created under
/etc/systemd
for the service).
# /etc/systemd/system/nginx.service.d/override.conf
[Service]
LimitNOFILE=5
Try restarting the nginx service and observe what happens.
Note that this parameter is usually used to increase the maximum number of open files, not reduce it - this is just an example to demonstrate the parameter.
journald
Logs captured by systemd from the services are kept in the system journal. The
logs kept in the journal can be easily queried using the journalctl
command.
In its most basic form, the journalctl
command displays logs from the
beginning of this system boot. We can use the -b
flag followed by a number to
get the logs associated with a specific previous boot. For example, -b -1
for
the logs of the previous boot.
Logs are not persisted by the default configuration. We must edit the
/etc/systemd/journald.conf
file and set the Storage parameter to
persistent
in order to force the journal to create the appropriate
permanent files to store the logs.
Edit the file and restart the systemd-journald
service. Reboot the system a
couple of times before inspecting the logs for each boot cycle.
Persistent logs are especially important to inspect what may have caused a system crash in a previous boot cycle.
When trying to monitor service errors, the following parameters are particularly useful:
-e
moves to the end of the log immediately (we do not usually need to inspect old logs, and this option can be used to skip to the latest logs);-x
adds explanations for some messages from a message catalog;-u unitname
only shows messages associated with a specific systemd unit (e.g., service);-t syslogtag
displays messages with a specific syslog tag associated with them (i.e., the text after the hostname in the logs, next to a number - for examplenginx
in this line:Feb 30 00:00:00 scgc-services nginx[XXXXX]
;-n number
only shows the most recent number logs;-f
makes the query follow updates to the logs, so new lines are displayed immediately.
Use the correct flags to show the logs for the nginx
service and automatically
update the displayed information.
From a different terminal update the /etc/nginx/nginx.conf
file and break the
configuration (e.g., remove a ;
character somewhere), and then restart the
service. Observe the logged information in the first terminal.
Task automation using cron
Some tasks must be performed periodically for various purposes - creating backups of configuration files, sending emails, cleaning up redundant information, etc..
There are multiple ways to configure repeating tasks, but the most common is
using cron
. Cron has a simple syntax for configuring tasks, with five fields
describing when the task should run
and the remaining parameters define the command that should run. To edit the
list of tasks for the current user, use crontab -e
. The command has the same
format as it would in a terminal; for example the following line could be used
to backup the important
directory every minute:
* * * * * umask 0077; tar zcvf "/home/student/backups/$(date +"\%F-\%H-\%M-\%S").tar.gz" /home/student/important
The percent (%
) sign is a special character and must be escaped using
backslashes.
Create a task that counts how many regular files there are in the /usr/share
directory every two minutes. Use the find
command to list the regular files
and the wc
command to count how many lines are printed by find (i.e., this
will give you the number of files).
Now, you may be wondering what happens to the output. If you look in the
journal for the syslog tag CRON
you may see some lines that show this
warning:
(CRON) info (No MTA installed, discarding output)
By default cron gathers the process' standard output and error and emails them to the user. However, since the system does not have any mail services installed, the output is discarded automatically.
Install the postfix
package and configure it to only store the messages
locally.
After the scheduled tasks execute, check the /var/log/student
file to see the
logged information.
A mail is only created if the scheduled tasks output the data to the standard
output and error. You can explicitly redirect the output and error streams
to a file using 2>&1 >>/some/file
or completely discard them using 2>&1 >/dev/null
,
which will disable the creation of notification emails.
Automating tasks using systemd timers
systemd timers are another option to automate recurring tasks, as well as triggering tasks relative to other events (e.g., system boot time).
By default a timer triggers a unit with the same name as them - for example, a
service named servicename.service
is automatically triggered by
servicename.timer
.
The list of timers available on the system can be inspected using systemctl list-timers
. The output of the command displays the name of the timer, when the
last activation of the timer happened and when the next activation is scheduled,
as well as the name of the service that the timer activates.
In this section we will write a service that will periodically update the system's message of the day (the text that is displayed when connecting to the system).
By default the system displays a dynamic MOTD that is automatically generated when connecting. To make the output more clear we will disable it.
Edit the /etc/pam.d/sshd
file and comment the following line by prepending a
pound sign (#
) before it, or removing it entirely:
session optional pam_motd.so motd=/run/motd.dynamic
After updating the PAM file, connect to the virtual machine from a different terminal and observe how the output has changed.
Now that we have a clean canvas, let's implement a task that runs the following script every 2 minutes.
Place the following script in /usr/local/sbin/update-motd.sh
. When it runs it
updates the /etc/motd
file, which will be displayed when connecting to the
system.
#!/bin/sh
set -eu -o pipefail
fetch_info() {
echo "System information at $(date)"
echo
echo "Network interfaces:"
ip -br a s | grep -E '^(eth|enp|virbr)'
echo
echo "Disk usage:"
df -h /
echo
}
echo "Fetching system information"
fetch_info > /etc/motd
echo "Finished successfully"
Using the motd-news.service
and motd-news.timer
service and timer files
as reference, create a new service and timer unit files in
/etc/systemd/system
, for a service named motd-update
.
The script executed by the service does not have to have the executable bit set
on it. To run it, we can explicitly invoke the bash
interpretor using the
following execution parameter:
ExecStart=/bin/bash /usr/local/sbin/update-motd.sh
The timer should trigger the service to run every two minutes. Use
man systemd.time
and read the CALENDAR EVENTS section to find how the
OnCalendar
field should be set.
Make sure to remove the RandomizedDelaySec=12h
parameter from the timer to
not add random delays in the execution.
Enable only the timer. The service does not have to be enabled, as it will be automatically started by the timer periodically.
Confirm that you have the correctly set up the timer by listing the registered timers and checking the timer's status or the associated journal entry.
If you have misconfigured the timer, you can edit it using the following command (the timer must be restarted after being edited):
$ sudo systemctl edit --full motd-update.timer
If you have configured the timer and service correctly, you should be able to see lines in the service's logs that look like this:
Feb 30 00:00:00 scgc-services bash[XXXXX]: Fetching system information
Feb 30 00:00:00 scgc-services bash[XXXXX]: Finished successfully
This is because by default systemd uses the name of the executable that is
invoked by ExecStart
as the system log tag.
Change the service's system log tag to motd-update using the
SyslogIdentifier
parameter in the [Service]
section.
Use the --full
option when editing the service file, since this change is
relevant to the service as a whole, and not an override.
If the change is applied correctly, the logs from the next service run should look like this:
Feb 30 00:00:00 scgc-services update-motd[XXXXX]: Fetching system information
Feb 30 00:00:00 scgc-services update-motd[XXXXX]: Finished successfully