Adding Custom Services With systemd

[vc_row][vc_column][vc_column_text]systemd is a system and service manager for Linux similar to SysV init. Several Linux distributions, notably Red Hat Enterprise Linux, SuSE, CentOS, Fedora, Debian and Ubuntu, are adopting it for their current or future releases. You can now experience systemd on Fedora and Red Hat Enterprise Linux 7 Beta.

We decided to install customized versions of Apache httpd and Percona Server, both of which must be started on boot.  systemd configurations had to be created for these.  We based our configuration files on existing ones for Apache httpd and MySQL. Furthermore, we made sure that necessary directories for these software are created at boot time.

The procedures described below were done on a VMware installation of Red Hat Enterprise Linux 7 Beta. These procedures should work on Fedora 20 as well (we’ll verify this in the future).

First, we compile and install Apache httpd version 2.4.9 (we’ll write an article about this in the future). Our installation of Apache httpd is in /opt/apache. The configuration files are in /etc/opt/apache while the logs are in /var/opt/apache.  Note that this layout of the installation is the opt layout for Apache httpd which is specified during configuration of the source.

Let us take a look at the systemd configuration file for the built-in Red Hat httpd daemon:

[Unit]
Description=The Apache HTTP Server
After=network.target remote-fs.target nss-lookup.target
[Service]
Type=notify
EnvironmentFile=/etc/sysconfig/httpd
ExecStart=/usr/sbin/httpd $OPTIONS -DFOREGROUND
ExecReload=/usr/sbin/httpd $OPTIONS -k graceful
ExecStop=/bin/kill -WINCH ${MAINPID}
# We want systemd to give httpd some time to finish gracefully, but still want
# it to kill httpd after TimeoutStopSec if something went wrong during the
# graceful stop. Normally, Systemd sends SIGTERM signal right after the
# ExecStop, which would kill httpd. We are sending useless SIGCONT here to give
# httpd time to finish.
KillSignal=SIGCONT
PrivateTmp=true
[Install]
WantedBy=multi-user.target

The above file, named httpd.service, is found in /usr/lib/systemd/system.

We now create another file which we name as apache-httpd.service and place in /etc/systemd/system. Note that we must make sure that httpd.service is disabled. Let us disable it using the command:

sudo systemctl disable httpd.service

To check the status of a service, we can type the following command:

sudo systemctl status httpd.service

Now, our apache-httpd.service file for the custom Apache httpd installation contains the following:

[Unit]
Description=The Apache HTTP Server (akumubuild)
After=network.target remote-fs.target nss-lookup.target percona-server.service
[Service]
#Type=notify
Type=forking
#Type=dbus
#BusName=com.redhat.apache-httpd
EnvironmentFile=/etc/sysconfig/apache-httpd
#ExecStart=/opt/apache/sbin/httpd $OPTIONS -DFOREGROUND
ExecStart=/opt/apache/sbin/httpd $OPTIONS 
ExecReload=/opt/apache/sbin/httpd $OPTIONS -k graceful
ExecStop=/bin/kill -WINCH ${MAINPID}
# We want systemd to give httpd some time to finish gracefully, but still want
# it to kill httpd after TimeoutStopSec if something went wrong during the
# graceful stop. Normally, Systemd sends SIGTERM signal right after the
# ExecStop, which would kill httpd. We are sending useless SIGCONT here to give
# httpd time to finish.
KillSignal=SIGCONT
PrivateTmp=true
PIDFile=/var/opt/apache/run/httpd.pid
[Install]
WantedBy=multi-user.target

The original httpd.service was used as a template. Note the differences. For apache-httpd.service, we allow httpd to fork. The Reload command does not use a kill command. Instead, '-k graceful' is passed to httpd.  Note that we also refer to the percona-server.service which we will discuss later.

We enable apache-httpd.service using ‘sudo systemctl enable apache-httpd.service‘.  Again, use the status command of systemctl to check on the service.  If it isn’t started, issue the command ‘sudo systemctl start apache-httpd.service‘.  The ‘enable‘ command makes sure that the service is started at boot time while ‘start‘ simply runs the service.

We should see the following by checking the status (‘sudo systemctl status apache-httpd’) of our custom installation of Apache httpd:

apache-httpd.service - The Apache HTTP Server (akumubuild)
   Loaded: loaded (/usr/lib/systemd/system/apache-httpd.service; enabled)
   Active: active (running) since Thu 2014-04-03 00:44:31 EDT; 1 day 17h ago
 Main PID: 1942 (httpd)
   CGroup: /system.slice/apache-httpd.service
           ├─1942 /opt/apache/sbin/httpd
           ├─1998 /opt/apache/sbin/httpd
           ├─1999 /opt/apache/sbin/httpd
           ├─2048 /opt/apache/sbin/httpd
           ├─2049 /opt/apache/sbin/httpd
           ├─2050 /opt/apache/sbin/httpd
           └─3237 /opt/apache/sbin/httpd
Apr 03 00:44:30 akumu.hq.linuxunbound.com systemd[1]: PID file /var/opt/apache/run/httpd.pid not re...rt.
Apr 03 00:44:31 akumu.hq.linuxunbound.com systemd[1]: Started The Apache HTTP Server (akumubuild).
Hint: Some lines were ellipsized, use -l to show in full.

Some things to consider:

  • Make sure the file /etc/sysconfig/apache-httpd exists.  This doesn’t have to contain anything though you can add some optional parameters to httpd by assigning them to the shell variable OPTIONS.
  • The directories /var/opt/apache/logs and /var/opt/apache/run must exist.

[GARD align=”center”]

Percona Server for MySQL

The MySQL-compatible Percona Server should have the following configuration file in /usr/lib/systemd/system named percona-server.service:

[Unit]
Description=The Percona Server (MySQL)
# See https://bugzilla.redhat.com/show_bug.cgi?id=714426
After=network.target remote-fs.target nss-lookup.target
Before=apache-httpd.service
[Service]
#Type=notify
#Type=forking
#Type=dbus
Type=simple
EnvironmentFile=/etc/sysconfig/percona-server
ExecStart=/usr/local/mysql/bin/mysqld --defaults-file=/etc/my.cnf --user=mysql $OPTIONS 
#ExecStop=/usr/local/mysql/bin/mysqladmin -u root -p shutdown
ExecStop=/bin/kill -TERM ${MAINPID}
ExecReload=/bin/kill -HUP ${MAINPID}
PrivateTmp=true
PIDFile=/var/run/mysqld/mysqld.pid
[Install]
WantedBy=multi-user.target

Again, we must make sure that certain files and directories exist:

  • the file /etc/sysconfig/percona-server
  • the directory /var/log/mysqld (or whatever is specified in /etc/my.cnf)
  • the directory /var/run/mysqld

Take note that /var/run/mysqld is a temporary directory that is created at boot time and deleted at shutdown.  We need to create a file called /etc/tmpfiles.d/percona.conf which contains the following:

d /run/mysqld 0755 mysql mysql -

This configuration file creates the directory /run/mysqld with mode 0755 and ownership by user ‘mysql‘ and group ‘mysql‘.

Enable the Percona Server service using systemctl as you did for Apache httpd.  Once it is running, you should see something like this status:

percona-server.service - The Percona Server (MySQL)
   Loaded: loaded (/usr/lib/systemd/system/percona-server.service; enabled)
   Active: active (running) since Thu 2014-04-03 00:44:28 EDT; 1 day 17h ago
 Main PID: 1842 (mysqld)
   CGroup: /system.slice/percona-server.service
           └─1842 /usr/local/mysql/bin/mysqld --defaults-file=/etc/my.cnf --user=mysql
Apr 03 00:44:28 akumu.hq.linuxunbound.com systemd[1]: Started The Percona Server (MySQL).

More information on systemd can be found on their wiki.[/vc_column_text][/vc_column][/vc_row]

PHP 5.5.0 and Apache 2.4.4 on CentOS 6.4

In June of 2013, PHP 5.5.0 was released as suitable for production use.  Our internal site used Apache httpd and MediaWiki to test PHP 5.5.0.  Our OS was CentOS 6.4 (64-bit).

We compiled PHP 5.5.0 from scratch, with all the prerequisite packages downloaded and installed, using the following configuration:

#! /bin/sh
#
# Created by configure

'./configure' \
'--enable-intl' \
'--enable-cgi' \
'--with-apxs2=/opt/apache/bin/apxs' \
'--with-pear' \
'--with-libdir=lib64' \
'--with-curl=shared' \
'--with-openssl=shared' \
'--prefix=/opt/php5' \
'--with-gdbm=shared' \
'--enable-dba=shared' \
'--with-db4=shared' \
'--enable-ftp' \
'--with-gd=shared' \
'--with-imap=shared' \
'--with-kerberos=/usr' \
'--with-imap-ssl=shared' \
'--enable-sockets' \
'--enable-zip' \
'--with-jpeg-dir=/usr' \
'--with-png-dir=/usr' \
'--with-xpm-dir=/usr' \
'--with-zlib' \
'--with-zlib-dir=/usr' \
'--with-bz2=shared' \
'--enable-exif' \
'--enable-soap' \
'--with-gmp' \
'--with-mcrypt=/usr/local' \
'--with-mhash' \
'--enable-mbstring' \
'--with-mysql=mysqlnd' \
'--with-mysqli=mysqlnd' \
'--with-pdo-mysql=mysqlnd' \
'--with-snmp=shared' \
'--enable-wddx' \
'--enable-pcntl' \
'--with-xmlrpc=shared' \
'--with-xsl=shared' \
'--with-ldap=shared' \
'--with-ldap-sasl' \
'--with-libedit' \
'--with-readline' \
'--with-pgsql=/usr/local/pgsql/bin/pg_config' \
'--with-pdo-pgsql=/usr/local/pgsql/bin/pg_config' \
"$@"

The configuration above assumed the following:

Other packages which were required by PHP were installed from either the official CentOS yum repository or from each package’s respective official source.

[GARD align=”center”]

The above configure script was saved as config-php.sh and placed above the PHP source directory.  The following commands were issued to start compiling and testing PHP:

cd php-5.5.0
../config-php.sh
make
make test

After the PHP tests were completed, the results were installed in /opt/php5:

sudo make install

In our setup, we used mod_fcgid to execute PHP scripts. See the previous article on installing PHP 5.4 for more information.

To verify that PHP is configured correctly, create a PHP script that contains the following:

<?php

phpinfo();

?>

The above script should generate something similar to this:
PHP Info

As you scroll down, you’ll see more information about the features of your particular PHP installation.

Remember to edit /opt/php/lib/php.ini to set certain variables properly. Here’s a sample php.ini file:

; Choose your preferred time zone and set it here
date.timezone = UTC

; Load extensions (see /opt/php5/lib/php/extensions)
; that you need.
extension = bz2.so
extension = curl.so
extension = dba.so
extension = gd.so
extension = imap.so
extension = ldap.so
extension = openssl.so
extension = snmp.so
extension = xmlrpc.so
extension = xsl.so

You should see information about these extensions in the phpinfo() output.