Using systemd-nspawn for PHP containers (and more)

systemd-nspawn can be used to run processes in a container (by mounting a debootstrap directory), without the need for Docker or other virtualisation tools. They’re often well suited for running updated packages on an OS which may not otherwise support it by default.

One use-case I’ve often had is to install PHP7 on a Debian Jessie machine via a Debian Stretch container, without modifying PHP directly on the host to ensure a future dist-upgrade runs smoothly.

We’ll first need a more recent version of systemd which supports the packages required for systemd-nspawn:

sudo -s
echo "deb http://archive.debian.org/debian jessie-backports main contrib non-free" >> /etc/apt/sources.list.d/ftp_debian_org_debian.list
echo 'Acquire::Check-Valid-Until "0";' > /etc/apt/apt.conf.d/10no--check-valid-until
apt-get update
apt-get -t jessie-backports install -y systemd systemd-container debootstrap

We then install a minimal Debian Stretch system with debootstrap:

debootstrap stretch /var/lib/container/stretch http://deb.debian.org/debian/

www-data will need access to the PHP 7 socket:

chmod 755 /var/lib/container

Next, a systemd service to manage the container (Note the /srv bind mount, replace this with your site directory as needed):

cat << EOF > /etc/systemd/system/stretchcontainer.service

[Unit]
# mysql.service parameters are only needed if you are using the /run/mysqld bind mount. This ensure that the container is only started after mysql, ensuring the socket is available
Description=Container stretchcontainer
After=mysql.service
Requires=mysql.service
PartOf=mysql.service                                 

[Service]
ExecStart=/usr/bin/systemd-nspawn -b -D /var/lib/container/stretch --bind="/srv/" --bind="/run/mysqld/"
KillMode=mixed
Type=notify
RestartForceExitStatus=133
SuccessExitStatus=133

[Install]
WantedBy=multi-user.target
EOF

Boot the container manually and install the necessary packages:

/usr/bin/systemd-nspawn -D /var/lib/container/stretch --bind="/srv/" /bin/bash
apt-get install -y dbus php7.0-{fpm,curl,gd,imagick,json,mbstring,mcrypt,mysql,xml,xmlrpc,zip}

If you want to use a non-standard version of PHP, you may want to use the Sury repository instead:

apt install ca-certificates apt-transport-https
wget -q https://packages.sury.org/php/apt.gpg -O- | sudo apt-key add -
echo "deb https://packages.sury.org/php/ stretch main" | sudo tee /etc/apt/sources.list.d/php.list
apt-get update
apt-get install -y dbus php7.2-{fpm,curl,gd,json,mbstring,mysql,xml,xmlrpc,zip}

If you need imagick or mcrypt they’re separate in later versions of PHP:

sudo apt-get install php-imagick
apt-get -y install gcc make autoconf libc-dev pkg-config
apt-get -y install libmcrypt-dev
apt-get -y install php-pear php7.2-dev
pecl install mcrypt-1.0.1
bash -c "echo extension=/usr/lib/php/20170718/mcrypt.so > /etc/php/7.2/mods-available/mcrypt.ini"
phpenmod mcrypt

Configure the PHP socket location and exit back to the host:

mkdir /opt/php
sed -i 's|/run/php/php7.0-fpm.sock|/opt/php/php7.0-fpm.sock|' /etc/php/7.0/fpm/pool.d/www.conf
exit

Symlink the socket location for ease of access on the host:

ln -s /var/lib/container/stretch/opt/php/ /opt/php

Enable PHP7 in the Apache vhosts (This example is taken from a machine running Symbiosis, but should be fairly easy to adjust to suit your setup):

sed -i '/<\/LocationMatch>/a\ \n \t<FilesMatch php$> \n \t\tSetHandler "proxy:unix:/opt/php/php7.0-fpm.sock|fcgi://127.0.0.1:9000"\n \t<\/FilesMatch>' /etc/symbiosis/apache.d/non_ssl.template.erb /etc/symbiosis/apache.d/ssl.template.erb

Alternatively, you can enable PHP7 in .htaccess instead:

<FilesMatch php$>
        SetHandler "proxy:unix:/opt/php/php7.0-fpm.sock|fcgi://127.0.0.1:9000"
</FilesMatch>

Start everything going and test!

a2enmod proxy_fcgi
systemctl enable stretchcontainer.service
systemctl start stretchcontainer.service
symbiosis-httpd-configure -vf
service apache2 restart

Containers can be managed via machinectl. There’s a few useful commands here, but to enter a container, use:

  machinectl list
  machinectl shell MACHINE
  bash

Pressing ^] 3 times, or the usual ^d will boot you out of the container and back onto the host.

Leave a Reply

Your email address will not be published. Required fields are marked *