11.12.15

Installing PHP 7.0.0 from scratch on CentOS 7.1


PHP 7.0.0 came out in early December. Since I've been blathering on about building bleeding edge web server software, let's take it for a run. My favourite distro, CentOS 7.1, doesn't have it available in packages yet, so let's build it from sources!


Prerequisites


First, let's assume that you've gone ahead and got (Apache's httpd)[http://shaniber.blogspot.ca/2015/12/installing-apache-httpd-with-http2.html] installed. We did that earlier (link to last blog post) when we set up httpd with HTTP/2 support. If you haven't done that yet, go do it. It's important.


We need some softwares to compile PHP 7 against, so install them first:


yum install -y mysql-devel libxml2-devel bzip2-devel curl-devel libjpeg-devel libpng-devel freetype-devel libc-client-devel libmcrypt-devel sqlite-devel libedit-devel net-snmp-devel systemd-devel mariadb mariadb-server mariadb-devel libxslt-devel

Installation


Now grab the archive, extract it, and get to work compiling it.


wget http://ca3.php.net/distributions/php-7.0.0.tar.gz && tar xf php-7.0.0.tar.gz
cd php-7.0.0
./configure --prefix=/usr/local \
        --sysconfdir=/etc \
        --localstatedir=/var \
        --enable-fpm \
        --with-fpm-systemd \
        --with-fpm-user=php \
        --with-fpm-group=www \
        --with-config-file-path=/etc \
        --with-config-file-scan-dir=/etc/php.d \
        --with-libxml-dir=/usr \
        --with-openssl=/usr/local/openssl-1.0.2d \
        --with-pcre-regex \
        --with-zlib \
        --enable-bcmath \
        --with-bz2=/usr \
        --enable-calendar \
        --with-curl=/usr \
        --enable-exif \
        --with-gd \
        --enable-mbstring \
        --with-mcrypt \
        --with-jpeg-dir=/usr \
        --with-png-dir=/usr \
        --enable-zip \
        --enable-ftp \
        --with-kerberos \
        --with-gettext \
        --with-xmlrpc \
        --with-xsl \
        --enable-opcache \
        --enable-mysqlnd \
        --with-pear 
make
make test
make install

Configuration


We'll set up php the new-fangled way, using FastCGI Process Manager, aka PHP-FPM. I mean, we've got that (server set up with HTTP/2)[http://shaniber.blogspot.ca/2015/12/installing-apache-httpd-with-http2.html] already, and we've got bleeding edge PHP 7.0.0, so let's go all the way, right? (Not really new fangled, PHP-FPM has been around for a while, but let's be honest, mod_php is generally how it's done.)


We defined a 'php' user during the configuration step for PHP to run as, so we should also create it:


useradd -g www -m php

This is just a basic set up, no VirtualHosts, everything right in the main httpd config, with a single pool of FastCGI processes wfrom which to serve PHP.


Set up PHP-FPM


In order to have httpd access PHP, we need to define a PHP-FPM worker. Based on our configuration above, we should find a default configuration file and directory in /etc. Here's a stripped down version of php-fpm.conf that works for me. Edit your file to match your environment. In this case, the php-fpm.log will end up /usr/local/log, so make sure that it exists, as well.


/etc/php-fpm.conf:
;; Global Options
[global]
pid = /var/run/php-fpm.pid
error_log = log/php-fpm.log
log_level = debug

;; Pool Definitions
include=/etc/php-fpm.d/*.conf

Now we need to set up the worker pool. There's varying opinions on the best way to do this, but we're just going to go with a basic dynamic set up for the moment. We can set up multiple pools to serve for different domains, and each must have a unique name, and must listen on a unique port. This will spawn several worker children once it's started up to handle requests.


cd /etc/php-fpm.d
cp www.conf.default example_com.conf

/etc/php-fpm.d/example_com.conf:
[example_com]
user = php
group = www
listen = 127.0.0.1:9000
pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
access.log = log/$pool.access.log
access.format = "%R - %u %t \"%m %r%Q%q\" %s %f %{mili}d %{kilo}M %C%%"
php_admin_value[error_log] = /var/log/fpm-php.$pool.log
php_admin_flag[log_errors] = on

php.ini configuration


Based on our configuration above, PHP will look for its main configuration file in /etc, and supplemental configuration files in /etc/php.d. Copy one of the versions (development or production) from .../{src}/php-7.0.0/. I'm using the development version. If you're installing this on a production machine, then use the production version.


cd /usr/local/src/php-7.0.0
cp php.ini-development /etc/php.ini

The php.ini file is huge, with numerous configuration settings. We'll just change a couple of settings to make it a little more useful, including setting a timezone, changing some memory limits, making the installation more secure, and enabling OPCache. Remember that if you modify ini settings in your pool config (like we did above), they'll override anything you set here.


/etc/php.ini:
disable_functions = exec,passthru,shell_exec,system,proc_open,popen
expose_php = Off
max_execution_time = 30
memory_limit = 64M
date.timezone = UTC
error_reporting = E_ALL & ~E_DEPRECATED
post_max_size = 5M
upload_max_filesize = 4M

opcache.enable=1
opcache.memory_consumption=64
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=7000
opcache.validate_timestamps=0 ;set this to 1 on production server
opcache.fast_shutdown=1

systemd configuration


Define a service that runs when systemd starts up. This file exists in your src directory, in php-7.0.0/sapi/fpm/php-fpm.service, but needs to be modified for your environment. (It feels like this should be installed automagically, but it wasn't for me). Copy or create it in /etc/systemd/system.


/etc/systemd/system/php-fpm.service:
[Unit]
Description=The PHP 7 FastCGI Process Manager
After=network.target

[Service]
Type=simple
PIDFile=/var/run/php-fpm.pid
ExecStart=/usr/local/sbin/php-fpm --nodaemonize --fpm-config /etc/php-fpm.conf
ExecReload=/bin/kill -USR2 $MAINPID

[Install]
WantedBy=multi-user.target

Now enable and start PHP-FPM.


systemctl enable php-fpm.service
systemctl start php-fpm.service

If you ask systemctl for the status of php-fpm, you should see a master process, and two pool processes for pool example_com, if you've been following along here.


Set up httpd access


Assuming you've gotten to this point, and everything seems to be working, we can now add PHP-FPM support to Apache httpd. We're proxying all files with the .php extention to the workers in the PHP-FPM pool we defined earlier.


First, make sure that the appropriate modules are enabled in /etc/httpd/conf/httpd.conf, and we include the php-fpm.conf file that we'll create later:


LoadModule proxy_module /usr/local/libexec/mod_proxy.so
LoadModule proxy_fcgi_module /usr/local/libexec/mod_proxy_fcgi.so

...

Include conf/extra/php-fpm.conf

Since we're just doing a very basic set up, and no VirtualHosts exist, we will just add the PHP-FPM configuration to the main server, in an extra config file.


/etc/httpd/conf/extra/php-fpm.conf:
<LocationMatch "^/(.*\.php(/.*)?)$">
  ProxyPass fcgi://127.0.0.1:9000/var/www/html/$1
</LocationMatch>

Make sure that the port (9000, here) matches the port you set above in your pool config file. Restart httpd, then add a simple file in your document root (which we set to /var/www/html in our last adventure):


/var/www/html/phpinfo.php:
<?php
    phpinfo();
?>

Now, hit the url for your server and load the file, and see if everything is awesome! If you see the usual PHP Info page, then you can celebrate your Great Success!


Remember now, though, that if you make a change to your PHP configuration in the php.ini file, or if you change your PHP-FPM set up, you'll need to reload your pool.


So what's next? Right now, we've got a pretty basic LAMP stack set up. We should probably abstract this out so that we can apply it to different VirtualHosts. We can also set up PHP-FPM with separate ''ondemand'' master pools for each VHost, for improved security. It would also be pretty nifty to make PHP-FPM communicate via systemd sockets, too.

8.12.15

Installing Apache httpd with HTTP/2 support from scratch on CentOS 7.1


On October 13, 2015, Apache http2 2.4.17 was released. This release, while just a point release, included modhttp2, a formalization within the Apache httpd software of modh2. Since CentOS 7.1, my preferred Linux distribution, doesn't yet include it, we need to compile it from source.

Dependencies


Since mod_http2 is dependent on nghttp2, we need to install this. We'll use the latest version, and compile it from source.

As well, we need a version of OpenSSL that supports ALPN. ALPN allows for application layer protocol negotiation, and is critical for clients to be able to negotiate the upgrade from HTTP 1.1 to HTTP/2. Again, since CentOS 7.1 only includes 1.0.1e, and we need at least 1.0.2, we have to compile it from source.

I'm assuming that you'll apply superuser permissions as appropriate to do things like make install, or moving / editing system files.

Prerequisites


There are several packages that we need to be able to build. We'll assume that we've got the Development group of tools installed.

  • zlib-devel is required by OpenSSL.
  • libev-devl is required by nghttpd.
  • pcre-devel is required by httpd.

System software required by the build.
: yum install -y zlib-devel libev-devel pcre-devel libxml2-devel

OpenSSL


Install the latest OpenSSL. Note that during the make-test phase, the tests will fail due to an expired test certificate, so you might just want to skip that phase.

wget https://www.openssl.org/source/openssl-1.0.2d.tar.gz && tar xf openssl-1.0.2d.tar.gz
cd openssl-1.0.2d
./config --prefix=/usr/local/openssl-1.0.2d shared zlib
make
make test
make install

Update the library path with the new libs.

echo "/usr/local/openssl-1.0.2d/lib " > /etc/ld.so.conf.d/openssl102d.conf
ldconfig

nghttp


Install the latest nghttp.

wget https://github.com/tatsuhiro-t/nghttp2/releases/download/v1.4.0/nghttp2-1.4.0.tar.gz && tar xf nghttp2-1.4.0.tar.gz
cd nghttp2-1.4.0
autoreconf -i
automake
autoconf
env OPENSSL_CFLAGS="-I/usr/local/openssl-1.0.2d/include" OPENSSL_LIBS="-L/usr/local/openssl-1.0.2d/lib -lssl -lcrypto" ./configure 
make
make install

Update the library path with the new libs.

echo "/usr/local/lib " > /etc/ld.so.conf.d/usr-local-lib.conf
ldconfig

APR / APR-util


The latest APR and APR-util are required to build httpd 2.4.x from source.

wget http://apache.mirror.vexxhost.com/apr/apr-1.5.2.tar.gz
cd apr-1.5.2
./configure
make
make install

wget http://apache.mirror.vexxhost.com/apr/apr-util-1.5.4.tar.gz
cd apr-util-1.5.4
./configure --with-apr=/usr/local/apr
make
make install

Apache httpd


Now we come to the payoff. We install Apache httpd, at least 2.4.17 as of this writing. We're using a custom layout using the config.layout file. My prefered layout is based on the Fedora layout, but stuffed into /usr/local, what with us compiling it ourselves, and the sysconf dir in the standard /etc.

wget http://apache.mirror.rafal.ca//httpd/httpd-2.4.17.tar.gz
(optional) config.layout:

Local layout
<Layout Local>
    prefix:        /usr/local
    exec_prefix:   ${prefix}
    bindir:        ${prefix}/bin
    sbindir:       ${prefix}/sbin
    libdir:        ${prefix}/lib
    libexecdir:    ${prefix}/libexec
    mandir:        ${prefix}/man
    sysconfdir:    /etc/httpd/conf
    datadir:       ${prefix}/share/httpd
    installbuilddir: ${libdir}/httpd/build
    errordir:      ${datadir}/error
    iconsdir:      ${datadir}/icons
    htdocsdir:     /var/www/html
    manualdir:     ${datadir}/manual
    cgidir:        /var/www/cgi-bin
    includedir:    ${prefix}/include/httpd
    localstatedir: /var
    runtimedir:    /run/httpd
    logfiledir:    ${localstatedir}/log/httpd
    proxycachedir: ${localstatedir}/cache/httpd/proxy
</Layout>

./configure --enable-http2 --enable-ssl --with-ssl=/usr/local --enable-so --enable-mpms-shared=all --with-apr=/usr/local/apr --with-apr-util=/usr/local/apr --enable-pie --with-pcre --enable-mods-shared=all --disable-distcache --disable-imagemap --enable-layout=Local
make
make install

Httpd really should have its own user, so add the www group and apache user.

groupadd www
useradd -g www -m apache

Now you'll need to edit your httpd.conf, to make sure that it's set up properly. You need to enable the three following modules, http2, socache_shmcb, and ssl. As well, since we're doing ssl, using the worker module would give us the best performance, so make sure that it's enabled.

LoadModule http2_module /usr/libexec/mod_http2.so
LoadModule socache_shmcb_module /usr/libexec/mod_socache_shmcb.so
LoadModule ssl_module /usr/libexec/mod_ssl.so

Since we're on CentOS 7, and systemd is the law of the land, you need to add a service, then enable it.

/etc/systemd/system/httpd.service:
[Unit]
Description=The Apache HTTP Server

[Service]
Type=forking
EnvironmentFile=/etc/sysconfig/httpd
PIDFile=/run/httpd/httpd.pid
ExecStart=/usr/local/apache2/bin/apachectl start
ExecReload=/usr/local/apache2/bin/apachectl graceful
ExecStop=/usr/local/apache2/bin/apachectl stop
KillSignal=SIGCONT
PrivateTmp=true

[Install]
WantedBy=multi-user.target

With this, we'll need to create the environment file:

/etc/sysconfig/httpd
#OPTIONS=
LANG=C

And now, we can enable:

systemctl enable httpd.service

It'll start now, but once you reboot, you'll find that httpd won't start, because it can't access /var/run/httpd. This time, we need to add a conf file to /etc/tmpfiles.d, so that the appropriate directory is created in the tmpfs in /run at boot.

/etc/tmpfiles.d/httpd.conf:
d /run/httpd   710 root www

You can now create a module configuration file for mod_http2, and populate it as such:

/etc/httpd/conf/extra/httpd_http2.conf:
<IfModule http2_module>
    LogLevel http2:debug
</IfModule>
# http
Protocols h2c http/1.1

You'll need to edit the ssl configuration as well, to add the https protocol. At the end of the section in /etc/httpd/conf/extra/httpd_ssl.conf, add:

# https
Protocols h2 http/1.1

This assumes that you've got a certificate for your server. If you don't, then creating a self-signed cert, or acquiring a legit cert, is left as an exercise to the reader. Just make sure that it's in place when you go to start httpd, which you will do now:

systemctl start httpd.service

It's now time to actually test this behemoth. We're going to skip testing for HTTP/2 connectivity over http, since that's largely academic. Modern browsers like Firefox only support HTTP/2 with TLS. Since we put an SSL certificate in place earlier, this shouldn't be a problem.

First, make sure you have a SPDY indicator add on installed in your browser. This will give you a little lightning bolt in the URL bar that will indicate the protocol that you've connected with. A couple of indicators for popular browsers are:


Now visit the root of your new site, and you ought to be greeted with a page full on PHP info. If you've gone to the https URL, then you should see a pretty little blue lightning bolt in your URL bar, indicating that your site is being served with HTTP/2.

References? Sure. I'll actually link these some day, probably.

  • https://blog.apar.jp/linux/3484/ (Japanese)
  • https://httpd.apache.org/docs/2.4/install.html
  • https://icing.github.io/mod_h2/howto.html
  • http://pixelinc.co/ubuntu-14-04-3-apache-http-2-web-server-setup/
  • http://blog.astaz3l.com/2015/02/09/how-to-install-apache-on-centos/
  • https://www.howtoforge.com/how-to-use-multiple-php-versions-php-fpm-and-fastcgi-with-ispconfig-3-ubuntu-12.04-lts-p3

7.6.15

Using the Toner Cartridge reset menu on a Brother HL-3140CDW printer

The Brother HL-3140CDW printer is a nifty, inexpensive colour laser printer (at least it was when I bought it). However, Brother has a bad practice of telling you that your toner cartridges are low when they're not.

To work around this, you can easily reset the toner counter, extending the life of your cartridges. It's pretty easy, it's just not in the user manual.

  • Power on your printer.
  • Life open the door.
  • Press and Hold Cancel then Secure, together, and release.

This should put your printer into the Reset menu.

  • Use the UP and DOWN arrows to navigate to the toner cartridge you want to reset.
  • Press OK to select it.
  • Press UP to reset.
  • Repeat for each toner cartridge you want to reset. The LCD will show Accepted after the reset has completed.

Close the printer door / lid when you're done, and then do a happy happy dance, because using this menu, I've been able to more than double the lifespan of my toner cartridges. This makes this machine a really good printer investment.

(Credit to this video for the instructions.)