├── .gitignore ├── LICENSE ├── README.md └── OptimumPHP.bash /.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore builds of PHP that wind up here after tests 2 | php-* 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 P'unk Avenue, LLC 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Optimum PHP 2 | 3 | ## Introduction 4 | 5 | Optimum PHP installs PHP (and Apache and MySQL, if not already present) 6 | according to best practices. That includes FastCGI to separate PHP page generation 7 | from static file delivery, APC to ensure your PHP framework is not compiled over and 8 | over on every page view, and the inclusion of support for MySQL, MongoDB, LDAP, GD 9 | and other important but frequently missing PHP features. 10 | 11 | For more information about PHP best practices, be sure to read the article 12 | [Faster, PHP! Kill! Kill!](http://punkave.com/window/2010/03/08/faster-php-kill-kill) 13 | 14 | ## Limitations 15 | 16 | This script is currently for Ubuntu and CentOS Linux systems. It has been tested with CentOS 5.6, Ubuntu 10.04 and 11.04. Pull requests to better support more CentOS and Ubuntu versions are welcome. We'll also consider pull requests for other Linux distributions if they don't make the script unmanageably large - let's talk about it. 17 | 18 | ## What You Get 19 | 20 | This script installs the specified version of PHP, from source, obtained 21 | directly from the official php.net mirrors. PHP is installed to 22 | `/usr/local/bin`. You get both the php commmand line binary and the PHP 23 | CGI/FastCGI binary. 24 | 25 | Your Apache server will be reconfigured to use FastCGI rather than mod_php 26 | for drastically better average performance when the server is also responsible 27 | for serving images, CSS, JavaScript, et cetera. A separate process pool for PHP 28 | means your server is not tying up expensive PHP processes just to deliver 29 | static images. See the article cited above for details. 30 | 31 | Your php.ini file will be tweaked with more realistic settings for Symfony, 32 | Drupal, Apostrophe and other PHP frameworks. If you're picky about these things, be 33 | sure to review what this script appends to `/usr/local/lib/php.ini`. 34 | 35 | Apache and MySQL are also installed if you don't already have them, as 36 | well as many required libraries for PHP. 37 | 38 | Your PHP build will include functions to access MySQL, MongoDB, LDAP and the gd 39 | graphics functions - all the critical yet often missing stuff. 40 | 41 | Only PHP and PHP extensions are installed from source. Everything else is 42 | installed via your operating system's package manager. We use pecl to install 43 | PHP packages wherever it's not broken, svn only when necessary. We don't believe 44 | in maintaining anything from source unless you have to, but having the latest 45 | version of PHP itself is important to us. (Read up on the performance benefits 46 | of PHP 5.4.x. They are significant.) 47 | 48 | ## Upgrading 49 | 50 | You can safely run this script more than once, for instance if you wish to upgrade to a 51 | newer release of PHP. 52 | 53 | ## Warnings 54 | 55 | Command line PHP will wind up in `/usr/local/bin`. Make sure your PATH looks 56 | there (even for cron jobs that invoke PHP). 57 | 58 | FastCGI does not support overriding php.ini via .htaccess files. If you have 59 | any php_value settings, Apache will display an error. Move those settings 60 | into php.ini. 61 | 62 | If you experience out of memory errors, edit: 63 | 64 | /var/local/fcgi/php-cgi-wrapper.fcgi 65 | 66 | Reduce the PHP_FCGI_CHILDREN setting to a smaller figure. Then restart Apache, 67 | or just do: 68 | 69 | killall php-cgi 70 | 71 | Then hit your server with some PHP traffic to make sure you've brought the 72 | setting down enough. Repeat if needed. You'll have best results if your server 73 | has some swap space available to provide headroom. 74 | 75 | Read http://punkave.com/window/2010/03/08/faster-php-kill-kill to understand 76 | everything that is happening here and how to deal with anything that doesn't 77 | work out. 78 | 79 | ## Usage Instructions 80 | 81 | To install PHP version 5.4.5 with 15 PHP processes, run this command as root. 82 | Most of the work will be done in `/tmp`, but you don't necesarily have to 83 | put `OptimumPHP.bash` there: 84 | 85 | bash OptimumPHP.bash 5.4.5 15 86 | 87 | NOTE: choosing the right number of PHP processes is quite important, so read 88 | on. 89 | 90 | "What's a PHP process?" Each PHP process is a continuously running php-cgi 91 | process that is reused to handle more connections (and periodically 92 | restarted). The number of PHP processes you have is the number of simultaneous 93 | requests your server can field without requiring any to wait. 94 | 95 | "How many PHP processes can I have?" If your PHP processes top out around 50MB, 96 | then 20 processes will take up 1GB, just about all the RAM on a server with 97 | 1GB of RAM (which must also run MySQL, Apache and so on). Do dial that back to 98 | 15 processes and you're in good shape. 99 | 100 | We strongly recommend a server with some swap space as this allows for some 101 | headroom for unexpected cases. 102 | 103 | We have encountered some VPSes (Virtual Private servers) that run out of 104 | memory twice as fast as they should with fastcgi. Other, newer configurations 105 | work fine. You'll have to experiment. 106 | 107 | "What happens if I pick too large a number?" If you have swap space on your 108 | server, it may just slow down. If you have no swap space, it is critical not 109 | to make this setting too large. If you experience out of memory errors, 110 | edit /var/local/fcgi/php-cgi-wrapper.fcgi and reduce the PHP_FCGI_CHILDREN 111 | setting to a smaller figure. Then restart Apache, or just do: 112 | 113 | killall php-cgi 114 | 115 | Then hit your server with some PHP traffic to make sure you've brought the 116 | setting down enough. Repeat if necessary until you find the sweet spot. 117 | 118 | "How big are my PHP processes?" Look at the RSIZE value reported in 'top' 119 | for your Apache processes (if you are using plain old mod_php - if you don't 120 | know, you probably have mod_php). If you are already using FastCGI, look at 121 | your PHP processes. You can subtract a little overhead from Apache processes 122 | since they do contain a bit of non-PHP related stuff. 123 | 124 | "Shouldn't I just run lots of PHP processes and use swap space?" Not really. 125 | If you have too much traffic you'll just grind your hard drive and slow down. 126 | Better to let connections queue up and wait gracefully than to completely 127 | crush your server to the point where you can't ssh in to tune it. 128 | 129 | "Why do you do (X) instead of (Y)?" This setup works well for us. You can 130 | read more about our rationale for it in the article I've cited twice already. 131 | Feedback is welcome there. 132 | 133 | ## Credits 134 | 135 | Optimum PHP was developed by P'unk Avenue, the creators of Apostrophe. 136 | Apostrophe is an open source content management system based on the Symfony 137 | PHP framework. You can bet we have a need for well-configured PHP servers. 138 | 139 | Learn more about the Apostrophe project here: 140 | 141 | [Apostrophe, the Open Source Project](http://apostrophenow.org) 142 | 143 | [Apostrophe Now, the Hosted Service](http://apostrophenow.com) 144 | 145 | ## Contact 146 | 147 | Tom Boutell mostly maintains this. Feel free to drop him a line at 148 | [tom@punkave.com](mailto:tom@punkave.com). 149 | -------------------------------------------------------------------------------- /OptimumPHP.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # READ THE README BEFORE ATTEMPTING TO USE THIS SCRIPT. 4 | # 5 | # OptimumPHP, Copyright 2012 P'unk Avenue, LLC. Released under the 6 | # terms of the MIT License. See the LICENSE file. 7 | # 8 | 9 | USAGE="Usage: update-php.bash version max-php-processes" 10 | 11 | if [ -z "$1" ] ; then 12 | echo $USAGE 13 | exit 1 14 | fi 15 | 16 | if [ -z "$2" ] ; then 17 | echo $USAGE 18 | exit 1 19 | fi 20 | 21 | VERSION=$1 22 | LIMIT=$2 23 | 24 | UBUNTU=`grep -i ubuntu /etc/lsb-release | wc -l` 25 | CENTOS="0" 26 | if [ "$UBUNTU" = "0" ] ; then 27 | CENTOS=`uname -a | grep -i centos | wc -l` 28 | if [ "$CENTOS" = "0" ] ; then 29 | CENTOS=`cat /etc/redhat-release | grep -i centos | wc -l` 30 | if [ "$CENTOS" = "0" ] ; then 31 | echo "This doesn't smell like either Ubuntu or CentOS. Giving up." 32 | exit 1 33 | fi 34 | fi 35 | fi 36 | 37 | if [ "$UBUNTU" != "0" ] ; then 38 | CODENAME=`cat /etc/lsb-release | grep DISTRIB_CODENAME | perl -p -e 's/DISTRIB_CODENAME=(\w+)/$1/'` 39 | 40 | if [ -z "$CODENAME" ] ; then 41 | echo "This doesn't smell like a Linux box (or Perl is not installed yet)." 42 | exit 1 43 | fi 44 | 45 | echo "Making sure gcc, Apache, MySQL and various libraries are installed" 46 | apt-get -y install build-essential apache2 libxml2-dev libcurl4-openssl-dev \ 47 | libcurl4-openssl-dev libjpeg-dev libpng-dev libfreetype6-dev libicu-dev \ 48 | libmcrypt-dev mysql-server mysql-client libmysqlclient-dev libxslt-dev \ 49 | autoconf libltdl-dev libldap2-dev postgresql-client libpq-dev || 50 | { echo "apt-get installs failed"; exit 1; } 51 | fi 52 | 53 | if [ "$CENTOS" != "0" ] ; then 54 | yum -y install gcc gcc-c++ httpd libxml2-devel curl-devel openssl-devel libidn-devel libjpeg-devel libpng-devel freetype-devel libicu-devel libmcrypt-devel mysql-server mysql mysql-devel libxslt-devel autoconf libtool-ltdl-devel httpd-devel apr-devel apr subversion openldap-devel postgresql-devel || 55 | { echo "yum installs failed"; exit 1; } 56 | cd /tmp && 57 | rm -rf fastcgi-compile && 58 | mkdir fastcgi-compile && 59 | cd fastcgi-compile && 60 | rm -rf mod_fastcgi* && 61 | wget http://www.fastcgi.com/dist/mod_fastcgi-current.tar.gz && 62 | tar -zxf mod_fastcgi-current.tar.gz && 63 | cd mod_fastcgi* && 64 | cp Makefile.AP2 Makefile && 65 | make top_dir=/usr/lib64/httpd && 66 | make install top_dir=/usr/lib64/httpd || 67 | { echo "fastcgi compile from source failed, fcgid won't do"; exit 1; } 68 | fi 69 | 70 | cd /tmp 71 | rm -f php-$VERSION.tar.gz && 72 | rm -rf php-$VERSION && 73 | # --trust-server-names doesn't exist in CentOS 5.6 build of wget 74 | # us.php.net is not a valid mirror anymore, use us3 75 | wget http://us3.php.net/get/php-$VERSION.tar.gz/from/us3.php.net/mirror -O php-$VERSION.tar.gz && 76 | tar -zxf php-$VERSION.tar.gz && 77 | cd php-$VERSION || ( echo "Could not download, untar and cd into php-$VERSION"; exit 1) 78 | 79 | UBUNTU=`grep -i ubuntu /etc/lsb-release | wc -l` 80 | if [ "$UBUNTU" != "0" ] ; then 81 | UVERSION=`lsb_release -r -s | cut -d. -f1` 82 | if [ "$UVERSION" -ge 12 ] ; then 83 | if [ `getconf LONG_BIT` = "64" ] ; then 84 | # Fix PHP configure script to find the LDAP libraries. There's no clean 85 | # parameter to find the libraries in a separate place just for LDAP 86 | # without losing the # includes, so hack the configure file 87 | echo "Fixing ./configure to find 64 bit LDAP libraries on Ubuntu" 88 | perl -pi -e 's/LDAP_LIBDIR=.*/LDAP_LIBDIR=\/usr\/lib\/x86_64-linux-gnu/g' ./configure 89 | fi 90 | fi 91 | fi 92 | 93 | # CGI (fastcgi) binary. Also installs CLI binary 94 | './configure' '--enable-cgi' '--enable-fastcgi' '--with-gd' '--with-pdo-mysql' '--with-curl' '--with-mysql' '--with-ldap' '--with-freetype-dir=/usr' '--with-jpeg-dir=/usr' '--with-mcrypt' '--with-zlib' '--enable-mbstring' '--enable-ftp' '--with-xsl' '--with-openssl' '--with-kerberos' '--enable-exif' '--enable-intl' '--with-pdo-pgsql' && 95 | #5.3.10 won't build in Ubuntu 11.10 without this additional library 96 | perl -pi -e 's/^EXTRA_LIBS = /EXTRA_LIBS = -lstdc++ /' Makefile 97 | make clean && 98 | # Make very sure we spot failed PHP builds 99 | make || { echo "PHP compile FAILED"; exit 1; } 100 | make install && 101 | pecl channel-update pecl.php.net && 102 | pecl config-set php_ini /usr/local/lib/php.ini 103 | 104 | echo "Installing pecl packages" 105 | 106 | # pecl's conf settings for tmp folders don't cover all of its 107 | # usages of /tmp and /var/tmp. So make sure /var/tmp allows exec. But don't 108 | # mess with it if someone has a tmp partition that isn't actually noexec, 109 | # or a noexec partition that isn't actually tmp 110 | TMPFS=`mount | grep /var/tmp | grep noexec | wc -l` 111 | if [ "$TMPFS" != "0" ] ; then 112 | echo "Temporarily enabling exec in /var/tmp since PECL is hardcoded to use it" 113 | mount -o,remount,rw,exec /var/tmp || { echo "Unable to remount /var/tmp with exec permissions"; exit 1; } 114 | fi 115 | 116 | echo $VERSION | grep ^5\.4 > /dev/null 117 | if [ $? -eq 0 ] ; then 118 | echo "APC pecl packages for 5.4.x are busted, building APC from svn" 119 | cd /tmp && 120 | rm -rf apc && 121 | svn checkout http://svn.php.net:/repository/pecl/apc/trunk apc && 122 | cd apc && 123 | phpize && 124 | ./configure --enable-apc-pthreadrwlocks && 125 | make && 126 | make install 127 | else 128 | echo "Not PHP 5.4.x, so we can use pecl for apc" 129 | printf "\n" | pecl install -f apc 130 | fi 131 | 132 | pecl install -f mongo 133 | 134 | # Regardless of whether any of that failed make sure we 135 | # put the tmp folder back to noexec 136 | if [ "$TMPFS" != "0" ] ; then 137 | echo "Re-disabling exec in /var/tmp" 138 | mount -o,remount,rw,noexec /var/tmp || { echo "Unable to remount /var/tmp with noexec permissions"; exit 1; } 139 | fi 140 | 141 | CONFIGURED=`grep '^extension=mongo.so' /usr/local/lib/php.ini` 142 | 143 | if [ -z "$CONFIGURED" ] ; then 144 | cat >> /usr/local/lib/php.ini <> /etc/apt/sources.list <> /etc/apt/sources.list < /etc/apache2/mods-available/fastcgi.conf < 238 | # One shared PHP-managed fastcgi for all sites 239 | Alias /fcgi /var/local/fcgi 240 | # IMPORTANT: without this we get more than one instance 241 | # of our wrapper, which itself spawns many PHP processes, so 242 | # that would be Bad (tm) 243 | FastCgiConfig -idle-timeout 20 -maxClassProcesses 1 244 | 245 | # Use the + so we don't clobber other options that 246 | # may be needed. You might want FollowSymLinks here 247 | Options +ExecCGI 248 | 249 | AddType application/x-httpd-php5 .php 250 | AddHandler fastcgi-script .fcgi 251 | Action application/x-httpd-php5 /fcgi/php-cgi-wrapper.fcgi 252 | 253 | EOM 254 | 255 | mkdir -p /var/local/fcgi/ && 256 | 257 | cat > /var/local/fcgi/php-cgi-wrapper.fcgi < /etc/httpd/conf.d/fastcgi.conf < 315 | # Use the + so we don't clobber other options that 316 | # may be needed. You might want FollowSymLinks here 317 | Options +ExecCGI 318 | 319 | AddType application/x-httpd-php5 .php 320 | AddHandler fastcgi-script .fcgi 321 | Action application/x-httpd-php5 /fcgi/php-cgi-wrapper.fcgi 322 | EOM 323 | 324 | mkdir -p /var/local/fcgi/ && 325 | 326 | cat > /var/local/fcgi/php-cgi-wrapper.fcgi <