rfc:socketactivation

This is an old revision of the document!


Request for Comments: systemd socket activation support for PHP-FPM

Introduction

Linux distributions with systemd support a “socket activation” feature that allows systemd to listen on the socket from early in the boot process and start the service when the first client connects. Supporting this in PHP-FPM is beneficial to systems with many pools (so they can start on-demand), for administrators that prefer to have a PHP-FPM pool listen on a privileged port or path without having to start it initially as root, and for administrators wanting to resolve a dependency between the web server accepting requests and PHP-FPM's socket being able to queue or service them.

launchd and legacy internet superservers support socket activation in similar ways.

Socket activation creates no overhead once the daemon has started. systemd does not proxy any traffic; it just hands over the file descriptor. Once the daemon is running and using the systemd-provided socket(s), there's no distinction in daemon operation until shutdown, where it skips closing the socket(s).

Implementation details

When systemd starts a socket-activated service, it passes the bound, listening socket in as a file descriptor. PHP-FPM is already accustomed to such behavior using the FPM_SOCKETS environmental variable. It's actually possible to “socket activate” PHP-FPM once by setting it cleverly (so that it looks for the file descriptor(s) created by systemd). Unfortunately, PHP-FPM's shutdown behavior leaves the systemd socket in a state that's unusable for subsequent activations.

It's possible to provide reliable socket activation support by having a loop before or after the “import inherited sockets” one in fpm_sockets_init_main(). The loop can either inspect the environmental variables systemd sends in directly or call the reference implementations defined in systemd/sd-daemon.h. Jerry and I have a patch working with the latter approach, including the necessary linking against systemd's library. (Linking is also desirable to ease adding support later for sd_notify(), which can inform systemd of PHP-FPM's current lifecycle status, including startup, reloading, and shutdown.)

Adding the support also requires marking the systemd-imported sockets as persistent past shutdown so PHP-FPM does not perform various cleanup actions on them that break subsequent activations of the service.

If systemd socket activation support isn't enabled at build time, this feature will have no effect. Even if this feature is enabled for a build, it will have no effect on non-users of socket activation.

Usage for end-users

The recommended use for PHP-FPM with socket activation is a 1:1:1 relationship between the listening socket unit in systemd, the service unit in systemd, and the PHP-FPM pool. This approach allows each pool to spawn on-demand. It also has nice effects like allowing cgroups resource limitations (like CPU shares, memory, block I/O, and network I/O) and security isolations (like PrivateTmp and syscall restrictions) to be set per-pool in the corresponding systemd service unit. Logs will also aggregate per-pool in the systemd journal for the corresponding service.

Here's an example configuration. It's still possible to have PHP-FPM drop its own permissions or have it perform its own daemonization fork, but it's unnecessary. It's also possible to directly start the PHP-FPM pool without waiting for the first incoming connection; systemd will still start the necessary socket and pass it in.

/etc/systemd/system/my-php-fpm-pool.socket

[Socket]
ListenStream=/var/run/my-php-fpm-pool.socket

[Install]
WantedBy=sockets.target

/etc/systemd/system/my-php-fpm-pool.service

[Service]
User=my-php-fpm-pool
Group=my-php-fpm-pool
ExecStart=/usr/sbin/php-fpm --fpm-config=/etc/php-fpm.d/my-php-fpm-pool.conf
KillMode=process

/etc/php-fpm.d/my-php-fpm-pool.conf

[global]
pid = /var/run/my-php-fpm-pool.pid        ; Not really used by anything with daemonize = no.
error_log = syslog                        ; Will aggregate to the service's systemd journal.
daemonize = no                            ; systemd handles the forking.

[www]
listen = /var/run/my-php-fpm-pool.socket  ; Must match systemd socket unit.
user = my-php-fpm-pool                    ; Ignored but required.
group = my-php-fpm-pool                   ; Ignored but required.
pm = static
pm.max_children = 10
slowlog = syslog

Enabling the pool

systemctl enable my-php-fpm-pool.socket # Enables the socket at system startup.
systemctl start my-php-fpm-pool.socket  # Starts the socket listening.

Potential Objections

Why not just use the ondemand process manager?

The ondemand process manager still keeps considerable memory allocated, and PHP-FPM currently has some idle CPU load (<1% per service, but it adds up when you manage 500+ pools on a box) when not processing requests.

The ondemand process manager doesn't solve the dependency issue mentioned earlier (a web server requiring PHP-FPM to be ready) or allow privileges to be dropped before PHP-FPM gets invoked at all. The latter is useful for platform providers that let users configure PHP-FPM for their individual use cases but want to provide assigned “listening” sockets.

Changelog

  • 2012-10-18: Patches added.
  • 2012-10-17: Initial version.
rfc/socketactivation.1350602517.txt.gz · Last modified: 2017/09/22 13:28 (external edit)