Sunday, April 7, 2013

How to write CentOS initialization scripts with Upstart

On Linux systems, initialization (init) scripts manage the state of system services during system startup and shutdown. When the system goes through its runlevels, the System V init system starts and stops services as configured. While this tried-and-true technology has been around since the dawn of Unix, you can now create modern and efficient CentOS 6 init scripts by using Upstart, an event-based replacement for System V init.
Until its latest release, CentOS used the System V init system by default. SysV init scripts are simple and reliable, and guarantee a certain order of starting and stopping.
Starting with version 6, however, CentOS has turned to a new and better init system – Upstart. Upstart is faster than System V init because it starts services simultaneously rather than one by one in a certain order. Upstart is also more flexible and robust, because it is event-based. Upstart generates events at various times, including while going through the system runlevels, similar to the SysV init system. However, Upstart may also generate custom events. For example, with Upstart you can generate an event that requires certain services to be started, regardless of the runlevel. And Upstart not only generates events, it also handles them – so, for example, when it acknowledges the event for starting a service it will do so. This event-based behavior is robust and fast.
Upstart supports SysV init scripts for compatibility reasons; most service init scripts in CentOS 6 continue to be SysV-based. You might someday have to create an init script yourself if you write custom software. If you do, you should write your new init scripts with Upstart in mind so you can benefit from the new init system's faster performance and additional features.

Beginning the Upstart init script

Upstart keeps init scripts in the /etc/init/ directory. A script's name should correspond to the name of the service or job it controls, with a .conf extension. The init script for the Tomcat service, for example, should be named /etc/init/tomcat.conf.
Like SysV init scripts, Upstart init scripts are regular Bash scripts, but extended with some Upstart-specific directives, which are called stanzas in Upstart. In SysV init scripts you commonly see the line . /etc/init.d/functions, which provides access to additional necessary SysV functions. Upstart scripts are more sophisticated and complete; you don't have to include any additional functions or libraries.
Just as in any Bash script, comments in Upstart scripts start with #. Put descriptive comments at the beginning of each script to explain its purpose, and in other places where the code may need explanation. You can use two special stanzas, author and description, for documentation.

Defining when a service starts

Tasks and services
Upstart manages two types of jobs: tasks and services. Tasks are short-lived processes that are expected to start, complete a task, then die. One example for such a task job is defined in /etc/init/control-alt-delete.conf in CentOS 6. It restarts the computer when a user presses the Control, Alt, and Delete keys.
In contrast to a task job, a service job handles a daemon or service, such as the Apache web service. This article focuses on service jobs.
After the introductory comments you can define when a service should start and stop using the special stanzas stop on and start on. These two stanzas can be used with a recognized Upstart event such as when the system enters a runlevel.
Usually administrators configure service jobs to start and stop with the server. By convention, in CentOS you should configure a service job to start at runlevels 2, 3, 4, and 5 and stop at runlevels 0, 1, and 6. In an Upstart init script this is written like this:
start on runlevel [2345]
stop on runlevel [06]
This instructs Upstart to start and stop the service whenever the system enters the runlevel in brackets.
Upstart also lets you start or stop services based on other types of events, such as the starting or stopping of other services. For example, suppose you have an Apache web server integrated with a Varnish caching web server, as described in the article Varnish improves web performance and security. In such a scenario you should make sure that Varnish starts whenever Apache starts, so the configuration stanza for Varnish should look like:
start on starting httpd
stop on stopped httpd
The latter stanza is an unique feature of Upstart; Upstart init scripts can stop services at the same time as other services are stopped, while SysV init scripts depend solely on runlevels.
Another difference is that you configure SysV init scripts when to start and stop by placing symlinks to them in the corresponding runlevels' directories in /etc/rcX.d/, where X is the runlevel number. The command chkconfig does this automatically for you in CentOS. While chkconfig continues to manage most of the init scripts in CentOS 6, it does not work with Upstart, and you cannot manage Upstart jobs with it.

Preparing for an Upstart job

To prepare and customize your environment for an Upstart service job you can use a few additional parameters, each on a new line in the job's .conf file:
  • respawn – When you use this parameter the service process will be restarted if it dies unexpectedly. Without this Upstart parameter you might have to write a dedicated wrapper program to start a service and ensure its proper and constant operation, such as mysqld_safe for starting MySQL.
  • expect fork – Every service job should be expected to fork in as a background process. When you specify this parameter Upstart obtains the new process's PID, which it can use later to send signals to it, such as to shut down or reload configuration.
  • kill timeout [seconds] – This is the number of seconds before the process may be forcibly killed. You should specify enough time (say, 120 seconds) so that interruption-sensitive services such as MySQL are able to complete any pending operations and shut down safely.
You can also adjust a few Bash variables in the job's .conf file. For example, you can configure the umask (permissions) with which new files will be created. A secure choice is umask 007, which means that new files will be created with restrictive permissions 770, which allow only the user itself and the members of its user group to manipulate the newly created files.
A special Upstart stanza pre-start allows you to specify a command or inline script to be run right before Upstart actually starts the job. This stanza is suitable for specifying any sanity checks, such the existence of a necessary file. You may also define prerequisite tasks, such as cleaning of the caching directory of a caching proxy. If the pre-start procedure fails – that is, if it exits with a code other than zero – then the whole job fails and the service is not started.
To see how this works, here's a pre-start directive to remove PHP accelerator eAccelerator's previously cached files, as you would want to do when eAccelerator is integrated with Apache: pre-start exec rm /var/cache/eaccelerator/* -rf. A longer example with a whole inline script looks like:
pre-start script
    # check if Apache's binary is executable or fail 
    [ -x /usr/sbin/httpd ]
    # clear the /tmp directory from old sessions
    rm /tmp/sess_*
end script
Several other stanzas are similar to the pre-start stanza:
  • post-start – specifies a procedure to run after starting the service. This is usually useful for complex services that may need additional attention after startup, such as MySQL.
  • pre-stop – specifies actions used in preparing for the service shutdown. It is rarely used.
  • post-stop – may be regarded as an alternative to the pre-start stanza; in some cases it makes more sense to take some actions right after service shutdown instead of waiting for its next start.

Configuring the Upstart start command or script

Once you've configured your environment, the last thing you must do is define the job's command or script using the stanzas script ... end script to write a regular Bash script inline, or just exec to simply execute a command with arguments. Here's an example that uses the script stanza for the rsyslog service in the rsyslog.conf file:
    . /etc/default/rsyslog
    exec rsyslogd $SYSLOGD_OPTIONS
end script
The above directive first sources (includes) the content of the file /etc/sysconfig/rsyslog, where the variable SYSLOGD_OPTIONS is defined. This variable is then used to start the rsyslogd service. This is a convenient way to start a service that requires complex or custom configuration, and it's why such script stanzas are suitable for services such as MySQL or Apache.
Alternatively, the exec stanza lets you specify an executable file and any additional arguments it may need. It's suitable for simpler services; for example, you can start the CUPS daemon with the directive exec /usr/sbin/cupsd -F.

How Upstart stops and reloads services

If you're familiar with SysV init scripts, you may wonder how you configure the commands to stop a service or reload its configuration. The answer is that you don't have to; with Upstart you only configure the start command for a service. When Upstart starts a service it keeps track of its PID and the PIDs of the process forks. When Upstart later needs to shut down a service, it does so with the native Unix signals. Upstart first sends a PID the SIGTERM signal to gracefully shut it down. If the process ignores SIGTERM, Upstart sends SIGKILL to forcibly kill it. Similarly, when the configuration needs to be reloaded, Upstart sends the SIGHUP signal. Upstart's simple architecture removes the needs to specify procedures for stopping, restarting, and reloading a service, though if the shutdown procedure for a service requires more than just sending SIGHUP or SIGTERM signals, you can use the pre-stop and post-stop stanzas.
One last and important difference between Upstart and SysV inits is how you manually start and stop jobs. Upstart works with the command /sbin/initctl, the init daemon control tool. It accepts as a first argument stop, start, restart, or reload, and changes the state of the service correspondingly. The second argument is the name of the service. For example, to start MySQL's Upstart job manually you would run the command initctl start mysqld.
I hope you can see the advantages of Upstart's powerful and sophisticated features. Today most default service jobs in CentOS 6 today remain SysV-based, though that will probably change over time. In Ubuntu, for example, Upstart was introduced in 2009, and today most of the init scripts have been migrated to Upstart.

No comments:

Post a Comment