A. 特点
1. 高效、内存使用少。
2. 权限分离,用户间互不干扰。
B. 应用程序说明
Nginx : 事件驱动的 Web 服务器,采用模块化设计,小巧、高效。
PHP-CGI : PHP的CGI接口版本(本文使用FastCGI高效接口)。
C. 整个架构的简单说明
Nginx 处理所有的 Web 请求,它将 PHP 的请求 Match 出,发送给上游服务器处理,这里的上游服务器就是 PHP-CGI。
PHP-CGI 工作在 FastCGI 模式,它侦听着一个地址端口(或 Unix socket文件,建议组合权限使用 Unix Socket 更安全),Nginx 会连接并发送请求及回收结果并发送给客户浏览器。
Nginx 运行于 www-data 用户环境,这要求 www-data 用户有所有虚拟主机用户的主目录访问权限。每个虚拟主机拥有自己的 PHP-CGI 进程组(PHP-CGI 可工作在多进程模式),运行于自己的用户环境,本方案并没有设计动态的 PHP-CGI 进程管理器用于对资源的负载均衡。
Nginx 使用了 HTTP OwnerMatch 模块,使得它能够控制每个虚拟主机的每个 Location 有哪些用户的文件的访问权限。关于这个模块
D. 以 Ubuntu 10.04 系统为例的配置实例
1. 安装应用程序
sudo apt-get install nginx mysql-server php5-cgi php5-mysql
PS:建议下载补丁版本的 Nginx => https://heiher.info/1755.html
2. 配置 Nginx
指定 Nginx 的进行用户,和工作进程数,其它根据实际需要作出调整。
sudo vim /etc/nginx/nginx.conf
user www-data; # 指定使用 www-data 执行 Nginx
worker_processes 2; # 指定 2 个子工作进程
3. 配置虚拟主机用户
a. 创建用户目录
sudo mkdir -p /var/web/username/{config,cert,bin,run}
b. 创建用户与组
本方案使用 sftp 作为用户的文件管理器,虚拟主机用户没有终端。sftp 方案见我的另一日志。
sudo useradd -m -g sftp -s /bin/false username
sudo mkdir -p /home/username/web/{www,logs}
sudo ln -s /home/username /var/web/username/home
c. 虚拟主机配置文件模板
/var/web/username/config/vhost 为虚拟主机配置文件,将软链接到 /etc/nginx/sites-enabled/ 目录中。以下是模板,修改其中的 username 和 server_name 值。
# vhost
# Heiher
# HTTP Server
server {
listen 80; ## listen for ipv4
server_name localhost;
access_log /var/web/username/home/web/logs/access.log;
error_log /var/web/username/home/web/logs/error.log;
location / {
root /var/web/username/home/web/www;
index index.html index.htm index.php;
## Rewrite
if (!-e $request_filename)
{
rewrite ^(.+)$ /index.php?q=$1 last;
}
omallow username sftp; # 允许访问隶属于 username:sftp 的文件
omdeny all; # 禁止访问其它所有文件
}
# pass the PHP scripts to FastCGI server listening on socket file
#
location ~ \.php$ {
if (!-e $request_filename) {
return 404;
}
fastcgi_pass unix:/var/web/username/run/pfw.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME /var/web/username/home/web/www/$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;
}
}
# HTTPS Server
server {
listen 443; ## listen for ipv4
server_name localhost;
ssl on;
ssl_certificate /var/web/username/cert/cert.pem;
ssl_certificate_key /var/web/username/cert/cert.key;
ssl_session_timeout 5m;
ssl_protocols SSLv3 TLSv1;
ssl_ciphers ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv3:+EXP;
ssl_prefer_server_ciphers on;
access_log /var/web/username/home/web/logs/access.log;
error_log /var/web/username/home/web/logs/error.log;
location / {
root /var/web/username/home/web/www;
index index.html index.htm index.php;
## Rewrite
if (!-e $request_filename)
{
rewrite ^(.+)$ /index.php?q=$1 last;
}
omallow username sftp;
omdeny all;
}
# pass the PHP scripts to FastCGI server listening on socket file
#
location ~ \.php$ {
if (!-e $request_filename) {
return 404;
}
fastcgi_pass unix:/var/web/username/run/pfw.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME /var/web/username/home/web/www/$fastcgi_script_name;
fastcgi_param HTTPS on;
include fastcgi_params;
}
# deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
location ~ /\.ht {
deny all;
}
}
d. php-fcgi 封装器源代码
这里的封装器只是设置了环境变量,这里同样需要修改 username。
保存下面的内容至文件 /var/web/username/bin/php-fcgi-wrapper
#!/bin/sh
# Heiher
USERNAME=username
PHP_CGI_BIN=/usr/bin/php-cgi
PHP_CGI_SOCKET=/var/web/${USERNAME}/run/pfw.sock
PHPRC=/etc/php5/cgi/
PHP_FCGI_MAX_REQUESTS=32768 # 工作进程最大处理请求数 (小内存建议设置为1,适当提高工作进程数)
PHP_FCGI_CHILDREN=4 # PHP-CGI 工作进程数,影响并发。(小内存建议设置为4)
export PHPRC
export PHP_FCGI_MAX_REQUESTS
export PHP_FCGI_CHILDREN
exec ${PHP_CGI_BIN} -b ${PHP_CGI_SOCKET}
exit 0
e. 安装 php-fcgd 服务
sudo vim /etc/init.d/php-fcgid # 输入以下内容
sudo update-rc.d php-fcgid defaults
脚本内容
#!/bin/sh
# Heiher
SCRIPT_OK=0
SCRIPT_ERROR=1
DESCRIPTION="php-fcgi super-duper-control thing"
NAME=php-fgcid
SCRIPT_NAME=$(basename $0)
webdir=/var/web
log_daemon_msg () {
echo [email protected]
}
log_end_msg () {
# Dummy function to be replaced by LSB library.
if test "$1" != "0"; then
echo "Error with $DESCRIPTION: $NAME"
fi
return $1
}
phpfcgid_start() {
echo "Starting $NAME."
for userdir in ${webdir}/*; do
user=$(basename ${userdir})
wrapper=${userdir}/bin/php-fcgi-wrapper
if [ -x ${wrapper} ]; then
su ${user} -c "${wrapper}&"
fi
done
}
phpfcgid_stop() {
echo "Stopping $NAME."
pkill php-cgi
}
phpfcgid_status() {
log_daemon_msg "To be implemented: status"
log_end_msg $SCRIPT_ERROR
}
parse_script_option_list () {
case "$1" in
start)
log_daemon_msg "Starting $DESCRIPTION" $NAME
if phpfcgid_start; then
log_end_msg $SCRIPT_OK
else
log_end_msg $SCRIPT_ERROR
fi
;;
stop)
log_daemon_msg "Stopping $DESCRIPTION" $NAME
if phpfcgid_stop; then
log_end_msg $SCRIPT_OK
else
log_end_msg $SCRIPT_ERROR
fi
;;
restart|force-reload)
log_daemon_msg "Restarting $DESCRIPTION" $NAME
if phpfcgid_stop; then
if phpfcgid_start; then
log_end_msg $SCRIPT_OK
else
log_end_msg $SCRIPT_ERROR
fi
else
log_end_msg $SCRIPT_ERROR
fi
;;
status)
phpfcgid_status
;;
*)
cat << EOF >&2
Usage: $SCRIPT_NAME {start|stop|restart|force-reload|status}
EOF
exit $SCRIPT_ERROR
;;
esac
}
parse_script_option_list [email protected]
f. 设置目录权限
这个步骤很关键,必须认真仔细设置,如无特殊需要,不要修改。如果我错了,感谢指正。
sudo chown -R www-data:www-data /var/web/username/config
sudo chmod 0755 /var/web/username/config
sudo chmod 0640 /var/web/username/cofnig/vhost
sudo chown -R www-data:www-data /var/web/username/cert
sudo chmod 0750 /var/web/username/cert
sudo chmod 0640 /var/web/username/cert/cert.pem
sudo chmod 0640 /var/web/username/cert/cert.key
sudo chown -R root:root /var/web/username/bin
sudo chmod -R 0755 /var/web/username/bin
sudo chown username:www-data /var/web/username/run
sudo chmod 0750 /var/web/username/run
sudo chown root:root /home/username
sudo chown -R username:www-data /home/username/web
sudo chmod 0755 /home/username
sudo chmod 0750 /home/username/web
# 注:/home/username/web/www 目录中的文件和目录全部要求是 username:sftp 隶属,文件权限 0644 目录权限 0755。
g. 启用此用户的虚拟主机
sudo ln -s /var/web/username/config/vhost /etc/nginx/sites-enabled/username
sudo nginx -t # 重启前测试脚本有没有错误
sudo service nginx reload
PS:所有的虚拟用户都隶属于 sftp 用户组,本方案建议使用 SFTP 作为用户的文件传输窗口。在 sshd_config 中对 sftp 组用户做必要的限制,如禁止转发和 Chroot 等等。
Over!
写的够详细了,下次就直接用了
博主好NB啊。。
Another great aspect about SFTP is the fact that it is much more reliable than unsecured FTP.
还是有地方看不懂啊
我觉得还是patch源码解决比较好
怎样的patch?你有什么好的想法吗?
哇,原来nginx wiki上的这篇文章是heiher哥写的!
哈,鹚终于搞懂内涵了:
PHP_CGI_SOCKET=/var/web/${USERNAME}/run/pfw.sock
Good post!
(please, next time write an english-version!!!)
啊啊啊,写读后感来了。
恕我直言,现在主流的虚拟主机需要给用户提供rewrite支持,
当然要是能提供对阉割版php.ini的支持更好。
从补丁和此页面的策略看,貌似无法支持。
—————————————————————————————
呃,我再检查了一下,用户的~目录属主已经变成了root:root,
/home/username只代表一个标志,
“家目录”意义大变(貌似不能终端登录,家目录意义本就已经变了)。
惯性思维……各位也仔细看看,/var/web/username/{bin,cert,conf,run}
这些子目录,权限设置非常微妙!
还有nginx.conf中应该有一句include /etc/nginx/sites-enabled/*的,
是整个用户虚拟主机运行的入口,才会运行Mucid兄的说的内涵。
用户到期貌似就是删除:
/etc/nginx/sites-enabled/username
/home/username
/var/web/username
即可
—————————————————————————————
再啰嗦下,用户rewrite和限制版php.ini支持,不知可有实现方案?
按博主这样的思路,应该会提供一个用户web表单提交rewrite和php.ini,
idc的客服提供接到表单后转交给技术支撑检查后再设置……
或者用户直接联系客服人工改……或者直接不支持。
还有mysql数据库位置,还有最头疼的资源占用,内存php能设置
进程最大占用还好说,磁盘quota、带宽、cpu……
见笑了。
当然 到期了 还有删除用户这操作……