Preface
There are many articles on web state that the scalability of Nginx is much better then Apache. We want to test building a hosting environment that use Nginx instead of Apache. And we can test the stability, scalability and etc on it. To maximize our control on the entire hosting environment, we'll compile everything from scratch. We'd also want to make binary backup-and-restore easier by putting everything inside "/usr/local" instead of default locations.
This is the summary on what we'll do:
- MySQL installation
- download the latest mysql community server (at the time, version 5.0.67) from web
- compile and install mysql to /usr/local/mysql-5.0.67
- put the data directory in /data/mysql-5.0 (for management practice)
- place the init.d file inplace
- start the mysql server
- PHP Fastcgi (with spawn-fcgi) installation
- download the latest php (at the time, version 5.3.0)
- compile and install php to /usr/local/php-5.3.0-fastcgi
- download the latest lighttpd (at the time, version 1.4.19)
- compile lighttpd
- copy "spawn-fcgi" from the compiled source of lighttpd to /usr/local/php-5.3.0-fastcgi/bin
- place the php fastcgi start script in place
- start the php fastcgi
- EngineX (Nginx) installation
- Testing and Have Fun :)
MySQL installation
- Download MySQL server
wget http://dev.mysql.com/get/Downloads/MySQL-5.0/mysql-5.0.67.tar.gz/from/ftp://ftp.iij.ad.jp/pub/db/mysql/
tar -xzf mysql-5.0.67.tar.gz
cd mysql-5.0.67
- Configuring MySQL Compilation
vi CONFIG.sh
Code#!/bin/bash# correct stupid permission
# in original package
FNM_OWN=$(readlink -f $0)
PWD_OWN=$(pwd)
cd ..
chown -R root.root $PWD_OWN
cd $PWD_OWN
chown -R root.root *
find -type f | xargs chmod 644
find -type d | xargs chmod 755
find -type f -name '*.sh' | xargs chmod 700
chmod 700 configure
chmod 700 install-sh
chmod 700 $FNM_OWN# configure
./configure \
--prefix="/usr/local/mysql-5.0.67" \
--enable-thread-safe-client \
--with-extra-charsets=all
chmod 700 CONFIG.sh
- Compile and Install MySQL
./CONFIG.sh && make && make install
- Create the init script (in Red Hat style)
vi /etc/init.d/mysqld.custom
Code#!/bin/bash
#
# mysqld This shell script takes care of starting and stopping
# the MySQL subsystem (mysqld).
#
# chkconfig: - 64 36
# description: MySQL database server.
# processname: mysqld
# config: $prefix/etc/my.cnf
# pidfile: /var/run/mysqld/mysqld.pid# Source function library.
. /etc/rc.d/init.d/functions# Source networking configuration.
. /etc/sysconfig/networkprog="MySQL (Custom Installed)"
prefix="/usr/local/mysql-5.0.67"
my_print_defaults="$prefix/bin/my_print_defaults"# extract value of a MySQL option from config files
# Usage: get_mysql_option SECTION VARNAME DEFAULT
# result is returned in $result
# We use my_print_defaults which prints all options from multiple files,
# with the more specific ones later; hence take the last match.
get_mysql_option(){
result=`$my_print_defaults "$1" | sed -n "s/^--$2=//p" | tail -n 1`
if [ -z "$result" ]; then
# not found, use default
result="$3"
fi
}get_mysql_option mysqld datadir "/var/lib/mysql"
datadir="$result"
get_mysql_option mysqld socket "$datadir/mysql.sock"
socketfile="$result"
get_mysql_option mysqld_safe log-error "/var/log/mysqld.log"
errlogfile="$result"
get_mysql_option mysqld_safe pid-file "/var/run/mysqld/mysqld.pid"
mypidfile="$result"start(){
touch "$errlogfile"
chown mysql:mysql "$errlogfile"
chmod 0640 "$errlogfile"
[ -x /sbin/restorecon ] && /sbin/restorecon "$errlogfile"
if [ ! -d "$datadir/mysql" ] ; then
action $"Initializing MySQL database: " $prefix/bin/mysql_install_db
ret=$?
chown -R mysql:mysql "$datadir"
if [ $ret -ne 0 ] ; then
return $ret
fi
fi
chown mysql:mysql "$datadir"
chmod 0755 "$datadir"
# Pass all the options determined above, to ensure consistent behavior.
# In many cases mysqld_safe would arrive at the same conclusions anyway
# but we need to be sure.
$prefix/bin/mysqld_safe --datadir="$datadir" --socket="$socketfile" \
--log-error="$errlogfile" --pid-file="$mypidfile" \
>/dev/null 2>&1 &
ret=$?
# Spin for a maximum of N seconds waiting for the server to come up.
# Rather than assuming we know a valid username, accept an "access
# denied" response as meaning the server is functioning.
if [ $ret -eq 0 ]; then
STARTTIMEOUT=30
while [ $STARTTIMEOUT -gt 0 ]; do
RESPONSE=`$prefix/bin/mysqladmin --socket="$socketfile" -uUNKNOWN_MYSQL_USER ping 2>&1` && break
echo "$RESPONSE" | grep -q "Access denied for user" && break
sleep 1
let STARTTIMEOUT=${STARTTIMEOUT}-1
done
if [ $STARTTIMEOUT -eq 0 ]; then
echo "Timeout error occurred trying to start MySQL Daemon."
action $"Starting $prog: " /bin/false
ret=1
else
action $"Starting $prog: " /bin/true
fi
else
action $"Starting $prog: " /bin/false
fi
[ $ret -eq 0 ] && touch /var/lock/subsys/mysqld
return $ret
}stop(){
MYSQLPID=`cat "$mypidfile" 2>/dev/null `
if [ -n "$MYSQLPID" ]; then
/bin/kill "$MYSQLPID" >/dev/null 2>&1
ret=$?
if [ $ret -eq 0 ]; then
STOPTIMEOUT=60
while [ $STOPTIMEOUT -gt 0 ]; do
/bin/kill -0 "$MYSQLPID" >/dev/null 2>&1 || break
sleep 1
let STOPTIMEOUT=${STOPTIMEOUT}-1
done
if [ $STOPTIMEOUT -eq 0 ]; then
echo "Timeout error occurred trying to stop MySQL Daemon."
ret=1
action $"Stopping $prog: " /bin/false
else
rm -f /var/lock/subsys/mysqld
rm -f "$socketfile"
action $"Stopping $prog: " /bin/true
fi
else
action $"Stopping $prog: " /bin/false
fi
else
ret=1
action $"Stopping $prog: " /bin/false
fi
return $ret
}restart(){
stop
start
}condrestart(){
[ -e /var/lock/subsys/mysqld ] && restart || :
}# See how we were called.
case "$1" in
start)
start
;;
stop)
stop
;;
status)
status mysqld
;;
restart)
restart
;;
condrestart)
condrestart
;;
*)
echo $"Usage: $0 {start|stop|status|condrestart|restart}"
exit 1
esacexit $?
chmod 755 mysqld.custom
groupadd -g 27 mysql
useradd -u 27 -g mysql -G mysql,daemon -d /var/lib/mysql -s /sbin/nologin -M mysql
mkdir /var/run/mysqld
chown mysql.mysql /var/run/mysqld
chmod 770 /var/run/mysqld
- Config MySQL (my.cnf)
mkdir /usr/local/mysql-5.0.67/etc
vi /usr/local/mysql-5.0.67/etc/my.cnf
Code[mysqld]
datadir=/data/mysql-5.0
socket=/tmp/mysql.sock
user=mysql[mysqld_safe]
log-error=/var/log/mysqld.log
pid-file=/var/run/mysqld/mysqld.pid
- Start MySQL
/etc/init.d/mysqld.custom start
PHP Fastcgi (with spawn-fcgi) Installation
After 2009/06, spawn-fcgi has become its own project, seperated from Lighttpd.
It should be available through here: http://redmine.lighttpd.net/projects/spawn-fcgi
- Download PHP
wget http://hk2.php.net/get/php-5.3.0.tar.bz2/from/hk.php.net/mirror
tar -xjf php-5.3.0.tar.bz2
cd php-5.3.0
- Configuring PHP Compilation
vi CONFIG.sh
Code#!/bin/bash./configure \
--prefix="/usr/local/php-5.3.0-fastcgi" \
--with-mysql="/usr/local/mysql-5.0.67" \
--with-gd \
--with-ttf \
--with-openssl \
--enable-mbstring \
--enable-fastcgi
- Compile and Install PHP
chmod 700 CONFIG.sh
./CONFIG.sh && make && make install
- Download Lighttpd
wget http://www.lighttpd.net/download/spawn-fcgi-1.6.2.tar.bz2
tar -xjf spawn-fcgi-1.6.2.tar.bz2
cd spawn-fcgi-1.6.2
- Configure Lighttpd Compilation
vi CONFIG.sh
Code#!/bin/bash./configure \
--prefix="/usr/local/php-5.3.0-fastcgi"
- Compile and install
chmod 700 CONFIG.sh
./CONFIG.sh && make && make install
- Create the init script
vi /etc/init.d/php-fastcgi
Code#!/bin/sh
#
# Spawn a fcgi processes
# written by LAKostis <lakostis at altlinux.ru>
#
# chkconfig: 345 48 52
# description: Spawn a fcgi processes
# processname: /usr/bin/spawn-fcgi
# config: /etc/sysconfig/spawn-fcgi
# pidfile: /var/run/spawn-fcgi.pidWITHOUT_RC_COMPAT=1
# Source function library.
. /etc/init.d/functionsPROG="PHP FastCGI (with spawn-fcgi)"
NAME=php-fastcgi
PREFIX=/usr/local/php-5.3.0-fastcgi
PIDFILE=/var/run/php-fastcgi.pid
LOCKFILE=/var/lock/subsys/php-fastcgi.lock## ABSOLUTE path to the spawn-fcgi binary
SPAWNFCGI="$PREFIX/bin/spawn-fcgi"## ABSOLUTE path to the PHP binary
FCGIPROGRAM="$PREFIX/bin/php-cgi"## bind to tcp-port on localhost
FCGISOCKET="/var/fastcgi/php-fastcgi.sock"## number of PHP childs to spawn in addition to the default. Minimum of 2.
## Actual childs = PHP_FCGI_CHILDREN + 1
PHP_FCGI_CHILDREN=20## number of request server by a single php-process until is will be restarted
PHP_FCGI_MAX_REQUESTS=1000## IP adresses where PHP should access server connections from
FCGI_WEB_SERVER_ADDRS="127.0.0.1"# allowed environment variables sperated by spaces
ALLOWED_ENV="PATH USER"## if this script is run as root switch to the following user
USERID=daemon
GROUPID=daemon################## no config below this line
RETVAL=0
start() {
echo -n $"Starting $prog: "if test x$PHP_FCGI_CHILDREN = x; then
PHP_FCGI_CHILDREN=5
fiexport PHP_FCGI_MAX_REQUESTS
export FCGI_WEB_SERVER_ADDRSALLOWED_ENV="$ALLOWED_ENV PHP_FCGI_MAX_REQUESTS FCGI_WEB_SERVER_ADDRS"
if [ !-d "$FCGISOCKETDIR" ]; then
mkdir $FCGISOCKETDIR
chown $USERID.$GROUPID $FCGISOCKETDIR
chmod 700 $FCGISOCKETDIR
fiif test x$UID = x0; then
EX="$SPAWNFCGI -s $FCGISOCKET -f $FCGIPROGRAM -P $PIDFILE -u $USERID -g $GROUPID -C $PHP_FCGI_CHILDREN"
else
EX="$SPAWNFCGI -s $FCGISOCKET -f $FCGIPROGRAM -P $PIDFILE -C $PHP_FCGI_CHILDREN"
fi# copy the allowed environment variables
E=for i in $ALLOWED_ENV; do
E="$E $i=${!i}"
done# clean environment and set up a new one
env - $E $EX
chmod 770 $FCGISOCKETRETVAL=$?
echo
[ $RETVAL = 0 ] && touch ${LOCKFILE}
return $RETVAL
}stop() {
echo -n $"Stopping $PROG: "
killproc $NAME php-fastcgi
RETVAL=$?
echo
[ $RETVAL = 0 ] && rm -f ${LOCKFILE} ${PIDFILE}
}
reload() {
echo -n $"Reloading $PROG: "
killproc $NAME -HUP
RETVAL=$?
echo
}case "$1" in
start)
start
;;
stop)
stop
;;
restart)
stop
start
;;
status)
status --pidfile "$PIDFILE" --expect-user $USERID -- $PROG
RETVAL=$?
;;
*)
msg_usage "${0##*/} {start|stop|restart|status}"
RETVAL=1
esacexit $RETVAL
- Start the Fastcgi server
/etc/init.d/php-fastcgi start
EngineX (Nginx) Installation
- Download Nginx
wget http://sysoev.ru/nginx/nginx-0.6.32.tar.gz
tar -xzf nginx-0.6.32.tar.gz
cd nginx-0.6.32
- Configure Nginx Compilation
vi CONFIG.sh
Code#!/bin/bash./configure \
--prefix="/usr/local/nginx-0.6.32" \
--with-openssl="/usr" \
--with-md5="/usr" \
--with-sha1="/usr"
- Compile and Install Nginx
./CONFIG.sh && make && make install
- Create a simple restart script for Nginx
vi /usr/local/nginx-0.6.32/restart
Code#!/bin/bashNGINX_DIR="/usr/local/nginx-0.6.32"
NGINX_SBIN="$NGINX_DIR/sbin"stop() {
if [ "$(pidof nginx)" != "" ]; then
killall nginx
fi
}start() {
$NGINX_SBIN/nginx
}stop
start
- Start Nginx
/usr/local/nginx-0.6.32/restart.sh
- To further integrate it with RedHat base linux, you might add n init script like this
vi /etc/init.d/nginx
Code#!/bin/sh
#
# nginx - this script starts and stops the nginx daemon
#
# chkconfig: - 85 15
# description: Nginx is an HTTP(S) server, HTTP(S) reverse \
# proxy and IMAP/POP3 proxy server
# processname: nginx
# config: /etc/nginx/nginx.conf
# config: /etc/sysconfig/nginx
# pidfile: /var/run/nginx.pid# Source function library.
. /etc/rc.d/init.d/functions# Source networking configuration.
. /etc/sysconfig/network# Check that networking is up.
[ "$NETWORKING" = "no" ] && exit 0prefix="/usr/local/nginx-0.6.38"
nginx="$prefix/sbin/nginx"
prog=$(basename $nginx)NGINX_CONF_FILE="$prefix/conf/nginx.conf"
#[ -f /etc/sysconfig/nginx ] && . /etc/sysconfig/nginx
lockfile=/var/lock/subsys/nginx
start() {
[ -x $nginx ] || exit 5
[ -f $NGINX_CONF_FILE ] || exit 6
echo -n $"Starting $prog: "
daemon $nginx -c $NGINX_CONF_FILE
retval=$?
echo
[ $retval -eq 0 ] && touch $lockfile
return $retval
}stop() {
echo -n $"Stopping $prog: "
killproc $prog -QUIT
retval=$?
echo
[ $retval -eq 0 ] && rm -f $lockfile
return $retval
}restart() {
configtest || return $?
stop
sleep 1
start
}reload() {
configtest || return $?
echo -n $"Reloading $prog: "
killproc $nginx -HUP
RETVAL=$?
echo
}force_reload() {
restart
}configtest() {
$nginx -t -c $NGINX_CONF_FILE
}rh_status() {
status $prog
}rh_status_q() {
rh_status >/dev/null 2>&1
}case "$1" in
start)
rh_status_q && exit 0
$1
;;
stop)
rh_status_q || exit 0
$1
;;
restart|configtest)
$1
;;
reload)
rh_status_q || exit 7
$1
;;
force-reload)
force_reload
;;
status)
rh_status
;;
condrestart|try-restart)
rh_status_q || exit 0
;;
*)
echo $"Usage: $0 {start|stop|status|restart|condrestart|try-restart|reload|force-reload|configtest}"
exit 2
esacexit $RETVAL
chmod 755 /etc/init.d/nginx
chkconfig --add nginx
chkconfig nginx on
- Open a browser and test!!!
Testing LEMP with Drupal
To further test this hosting platform, I've tested Drupal installation with it.
Apache support .htaccess file that let user to define folder specific config directive. Nginx does not support this. So instead of just putting an Apache hosting site on Nginx virtual host, we need to config Nginx site-by-site. This is quite bothering, but you can't have everything at the same time. So we started the testing.
- Configuring Nginx for virtual hosting
The Default nginx.conf looks like this:
Code#user nobody;
worker_processes 1;#error_log logs/error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;#pid logs/nginx.pid;
events {
worker_connections 1024;
}http {
include mime.types;
default_type application/octet-stream;#log_format main '$remote_addr - $remote_user [$time_local] $request '
# '"$status" $body_bytes_sent "$http_referer" '
# '"$http_user_agent" "$http_x_forwarded_for"';#access_log logs/access.log main;
sendfile on;
#tcp_nopush on;#keepalive_timeout 0;
keepalive_timeout 65;#gzip on;
server {
listen 80;
server_name localhost;#charset koi8-r;
#access_log logs/host.access.log main;
location / {
root html;
index index.html index.htm;
}#error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}# proxy the PHP scripts to Apache listening on 127.0.0.1:80
#
#location ~ \.php$ {
# proxy_pass <a href="http://127.0.0.1;" title="http://127.0.0.1;">http://127.0.0.1;</a>
#}# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
#
#location ~ \.php$ {
# root html;
# fastcgi_pass 127.0.0.1:9000;
# fastcgi_index index.php;
# fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
# include fastcgi_params;
#}# deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
#location ~ /\.ht {
# deny all;
#}
}# another virtual host using mix of IP-, name-, and port-based configuration
#
#server {
# listen 8000;
# listen somename:8080;
# server_name somename alias another.alias;# location / {
# root html;
# index index.html index.htm;
# }
#}# HTTPS server
#
#server {
# listen 443;
# server_name localhost;# ssl on;
# ssl_certificate cert.pem;
# ssl_certificate_key cert.key;# ssl_session_timeout 5m;
# ssl_protocols SSLv2 SSLv3 TLSv1;
# ssl_ciphers ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP;
# ssl_prefer_server_ciphers on;# location / {
# root html;
# index index.html index.htm;
# }
#}}
This is really easy to read. To add a virtual host, you may simply add this into http directive
Codeserver {
listen 10.0.0.50:80; # change to fit your server
server_name example.com *.example.com; # change to the hostname you want
root /var/www/somewhere; # change to fit your hosting
error_page 500 502 503 504 /50x.html;location / {
index index.html index.htm;
}location = /50x.html {
root html; # you'll have default error pages here
}
}
- Configuring Nginx for PHP virtual hosting
- Configuring Nginx for Drupal (PHP virtual hosting with Rewrite)
(still writing...)
