一、Nginx理论
传统上基于进程或线程模型架构的web服务通过每进程或每线程处理并发连接请求,这势必会在网络和I/O操作时产生阻塞,其另一个必然结果则是对内存或CPU的利用率低下。生成一个新的进程/线程需要事先备好其运行时环境,这包括为其分配堆内存和栈内存,以及为其创建新的执行上下文等。这些操作都需要占用CPU,而且过多的进程/线程还会带来线程抖动或频繁的上下文切换,系统性能也会由此进一步下降。
在设计的最初阶段,nginx的主要着眼点就是其高性能以及对物理计算资源的高密度利用,因此其采用了不同的架构模型。受启发于多种操作系统设计中基于“事件”的高级处理机制,nginx采用了模块化、事件驱动、异步、单线程及非阻塞的架构,并大量采用了多路复用及事件通知机制。在nginx中,连接请求由为数不多的几个仅包含一个线程的进程worker以高效的回环(run-loop)机制进行处理,而每个worker可以并行处理数千个的并发连接及请求。
如果负载以CPU密集型应用为主,如SSL或压缩应用,则worker数应与CPU数相同;如果负载以IO密集型为主,如响应大量内容给客户端,则worker数应该为CPU个数的1.5或2倍。
Nginx会按需同时运行多个进程:一个主进程(master)和几个工作进程(worker),配置了缓存时还会有缓存加载器进程(cache loader)和缓存管理器进程(cache manager)等。所有进程均是仅含有一个线程,并主要通过“共享内存”的机制实现进程间通信。主进程以root用户身份运行,而worker、cache loader和cache manager均应以非特权用户身份运行。
主进程主要完成如下工作:
1. 读取并验正配置信息;
2. 创建、绑定及关闭套接字;
3. 启动、终止及维护worker进程的个数;
4. 无须中止服务而重新配置工作特性;
5. 控制非中断式程序升级,启用新的二进制程序并在需要时回滚至老版本;
6. 重新打开日志文件;
7. 编译嵌入式perl脚本;
worker进程主要完成的任务包括:
1. 接收、传入并处理来自客户端的连接;
2. 提供反向代理及过滤功能;
3. nginx任何能完成的其它任务;
cache loader进程主要完成的任务包括:
1. 检查缓存存储中的缓存对象;
2. 使用缓存元数据建立内存数据库;
二、主从模型
我是在rhel-server-6.4(64位)上做的,这里nginx1和nginx2上面分别安装上nginx和keepalived。
Nginx1 |
Nginx2 |
Vip1 |
172.16.58.1 |
172.16.58.6 |
172.16.58.7 |
1、安装yum库
我这里编译安装nginx需要实现需要安装开发包组,在红帽5上我需要安装开发组包"DevelopmentTools"和"DevelopmentLibraries",在红帽6上我用yum包安装的开发环境和红帽5的不太一样,需要安装"Developmenttools","ServerPlatform Development",有时候还需要安装"Compatibility libraries"(兼容库),另外还有可能会使用"Desktop Platform Development"。我们这里安装的光盘是rhel-server-6.4-x86_64-dvd,安装过程红帽5一样。
[root@localhost~]# mkdir /mnt/cdrom ###创建cdrom目录 [root@localhost~]# mount /dev/cdrom /mnt/cdrom ###挂载光盘(当场有效,开机无效) [root@localhost~]# vim /etc/fstab ###在配置文件中挂载光盘(当场无效,开机有效) /dev/cdrom /mnt/cdrom ext3 defaults 0 0 [root@localhost~]# cd /etc/yum.repos.d/ ###进入yum.repos.d目录下面 [[email protected]]# vim server.repo ###编辑server.repo [base] name=Server baseurl=file:///mnt/cdrom/Server enabled=1 gpgcheck=0
2、解决依赖关系
Nginx某些功能可能还要依赖pcre所以我还要安装pcre-devel这个包。Pcre是perl兼容的正则表达式库。
# yumgroupinstall "Server Platform Development" "Development tools"�Cy # yum -y installpcre-devel ###安装pcre-devel
3、安装nginx
我这里已经把软件包下载到本地nginx-1.4.1.tar.gz和keepalived-1.2.7.tar,下载完成后我首先要同步一下时间,如果系统时间比软件包的时间要靠前的话,系统就认为这个软件包是未来的,所以软件包是使用不了的。我知道nginx是轻量级的web服务器。
# date ###查看一下时间 # update 172.16.0.1 ###同步服务器时间 # tar xf nginx-1.4.1.tar.gz ###解压
Nginx基本上都是以普通用户进行运行,所以我要在这里建立一个用户,创建完成后就开始编译和安装了。
# groupadd -r -g 109 nginx ###创建组 # useradd -r -g 109 -u 109 nginx ###创建用户 # ./configure \ --prefix=/usr \ ###默认安装路径 --sbin-path=/usr/sbin/nginx \ ###nginx自身可执行程序的路径 --conf-path=/etc/nginx/nginx.conf \ ###主配置文件的路径 --error-log-path=/var/log/nginx/error.log \ ###错误日志文件路径 --http-log-path=/var/log/nginx/access.log \ ###访问日志文件路径 --pid-path=/var/run/nginx/nginx.pid \ ###pid文件路径 --lock-path=/var/lock/nginx.lock \ ###锁文件路径 --user=nginx \ ###用户名 --group=nginx \ ###组名 --with-http_ssl_module \ ###使用ssl模块 --with-http_flv_module \ ###使用flv模块 --with-http_stub_status_module \ --with-http_gzip_static_module \ ###使用gzip压缩模块 --http-client-body-temp-path=/var/tmp/nginx/client/ \ ###请求报文的主体放的位置 --http-proxy-temp-path=/var/tmp/nginx/proxy/ \ ###和代理相关的 --http-fastcgi-temp-path=/var/tmp/nginx/fcgi/ \ ###和fastcgi相关的 --http-uwsgi-temp-path=/var/tmp/nginx/uwsgi \ ###和uwsgi相关的 --http-scgi-temp-path=/var/tmp/nginx/scgi \ ###和scgi相关的 # make && make install
说明:如果想使用nginx的perl模块,可以通过为configure脚本添加--with-http_perl_module选项来实现,但目前此模块仍处于实验性使用阶段,可能会在运行中出现意外,因此,其实现方式这里不再介绍。如果想使用基于nginx的cgi功能,也可以基于FCGI来实现,具体实现方法请参照网上的文档。
4、提供服务脚本
新建文件/etc/rc.d/init.d/nginx内容如下:
#!/bin/sh # # nginx - this script starts and stops thenginx 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 0 nginx="/usr/sbin/nginx" prog=$(basename $nginx) NGINX_CONF_FILE="/etc/nginx/nginx.conf" [ -f /etc/sysconfig/nginx ] && ./etc/sysconfig/nginx lockfile=/var/lock/subsys/nginx make_dirs() { #make required directories user=`nginx -V 2>&1 | grep "configure arguments:" | sed's/[^*]*--user=\([^ ]*\).*/\1/g' -` options=`$nginx -V 2>&1 | grep 'configure arguments:'` for opt in $options; do if [ `echo $opt | grep '.*-temp-path'` ]; then value=`echo $opt | cut -d"=" -f 2` if [ ! -d "$value" ]; then # echo "creating"$value mkdir -p $value && chown-R $user $value fi fi done } start() { [-x $nginx ] || exit 5 [-f $NGINX_CONF_FILE ] || exit 6 make_dirs 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 esac # chmod +x /etc/rc.d/init.d/nginx ###赋予执行权限 # chkconfig --add nginx ###加入服务列表中 # chkconfig --list nginx ###查看状态 # service nginx start ###启动服务器
提示:这里有可能会报错,因为在不同的系统上,它们所指定的路径可能不一样,尤其是/var/tmp/nginx/下面,尤其是在这些路径下面没有这些文件目录的话可能会出错,如果出错了告诉你没有这些目录,我们要手动建立这些文件目录就行了。
5、测试网页
6、安装keepalived
这里我是编译安装keepalived,和上面我们yum安装的不一样,不过和nginx一样需要在nginx1和nginx2上都需要安装。我们可能在编译安装的时候会出错,这时候可能依赖的库时popt,我们需要用yum来安装,安装完成后我们在进行上面一步。
# tar xf keepalived-1.2.7.tar.gz ###解压 # cd keepalived-1.2.7 # ./configure --prefix=/usr/ # yum install -y popt-devel ###安装popt-devel # ./configure --prefix=/usr/ # make && make install # cp /usr/etc/rc.d/init.d/keepalived/etc/rc.d/init.d/keepalived ###复制服务脚本
# vim keepalived
(1)在nginx1上面(主服务器)上修改配置文件并测试
# vim /etc/keepalived/keepalived.conf ###配置文件 ! Configuration File for keepalived global_defs { notification_email { [email protected] [email protected] } notification_email_from [email protected] smtp_connect_timeout 3 smtp_server 127.0.0.1 router_id LVS_DEVEL } vrrp_script chk_nginx{ script "killall -0 nginx" interval 2 # check every 2 seconds weight -2 # if failed, decrease 2 of the priority fall 2 # require 2 failures for failures rise 1 # require 1 sucesses for ok } vrrp_script chk_schedown { script "[[ -f /etc/keepalived/down ]] && exit 1 || exit0" interval 2 weight -2 } vrrp_instance VI_1 { interface eth0 # interface for inside_network, bound by vrrp state MASTER # Initial state, MASTER|BACKUP # As soon as the other machine(s) come up, # an election will be held and the machine # with the highest "priority" will become MASTER. # So the entry here doesn't matter a whole lot. priority 101 # for electing MASTER, highestpriority wins. # to be MASTER, make 50 more than other machines. virtual_router_id 56 # arbitary unique number 0..255 # used to differentiate multiple instances of vrrpd # running on the same NIC (and hence same socket). garp_master_delay 1 authentication { auth_typePASS auth_pass password } track_interface { eth0 } # optional, monitor these as well. # go to FAULT state if any of these go down. virtual_ipaddress { 172.16.58.7/16 dev eth0 label eth0:1 } #addresses add|del on change to MASTER, to BACKUP. #With the same entries on other machines, #the opposite transition will be occuring. #<IPADDR>/<MASK> brd<IPADDR> dev <STRING> scope <SCOPE> label <LABEL> track_script { chk_nginx chk_schedown } notify_master "/etc/keepalived/notify.sh master" notify_backup "/etc/keepalived/notify.sh backup" notify_fault"/etc/keepalived/notify.sh fault" } #vrrp_instance VI_2 { # interface eth0 # state MASTER # BACKUP for slaverouters # priority 101 # 100 for BACKUP # virtual_router_id 52 # garp_master_delay 1 # # authentication { # auth_typePASS # auth_pass password # } # track_interface { # eth0 # } # virtual_ipaddress { # 172.16.58.2/16 dev eth0 label eth0:1 # } # track_script { # chk_nginx # chk_mantaince_down # } # # notify_master "/etc/keepalived/notify.sh master eth0:1" # notify_backup "/etc/keepalived/notify.sh backup eth0:1" # notify_fault "/etc/keepalived/notify.sh fault eth0:1" #}
(2)在nginx2上(从服务器)修改配置文件
这里只需要设置这么多就行了。
我这里自己设置的有脚本(实现健康检查),大家如果想配置,可使用下面这个脚本,如果没有这个脚本的话,我试验的时候还需要关闭keepalived服务,如果不关闭,仅关闭nginx,是不能实现地址飘移的,我们有了这个脚本,实现了为主从设备切换提供脚本,只要主服务器不在,就会实现地址飘移的。(上面有路径)脚本的内容如下:
#vim notify.sh 脚本内容如下: #!/bin/bash # description:An example of notify script # ifalias=${2:-eth0:0} interface=$(echo$ifalias | awk -F: '{print $1}') vip=$(ip addrshow $interface | grep $ifalias | awk '{print $2}') #contact='[email protected]' contact='root@localhost' workspace=$(dirname$0) notify() { subject="$ip change to $1" body="$ip change to $1 $(date '+%F%H:%M:%S')" echo $body | mail -s "$1transition" $contact } case"$1" in master) notify master exit 0 ;; backup) notify backup /etc/rc.d/init.d/httpd restart exit 0 ;; fault) notify fault exit 0 ;; *) echo 'Usage: $(basename $0){master|backup|fault}' exit 1 ;; esac # chmod +xnginx.sh
7、编辑并测试网页:
我们首先要查看一下配置文件,看一下我们的网页在什么地方,然后就创建网页,创建完成后,我们就重启服务器然后进行测试,看自己设置的网页是否有问题,然后在测试一下我用vip测试一下显示的页面,并以此判断我上面的设置是否正确。
# mkdir /etc/nginx/html # vim/etc/nginx/html/index.html <h1>nginx1.shuaige.com</h1> <h1>nginx2.shuaige.com</h1> # servicenginx restart
这个时候我将nginx1(主服务器)给停掉,我们这里看看虚拟ip是否会飘到从服务器上,我们可以从查看nginx1和nginx2的地址配置信息中得到(如果结果不变,我们有要多查看几次),如果还不信我们就用测试网页来检验我的实验是否正确。
# touch /etc/keepalived/down ###停止主服务器上的 # ifconfig
三、双主模式
1、规划
Nginx1 |
Nginx2 |
Vip1 |
Vip2 |
172.16.58.1 |
172.16.58.6 |
172.16.58.7 |
172.16.58.8 |
在上面基于web服务的高可用集群中,我们把keepalived配置文件中的最下面#开头的行开启了,开启后我们还要进行编辑,在nginx1中将VI_2,我需要将state MASTER改为state BACKUP,我们把优先级改为100,只要比MASTER的优先级小就行。内容如下:
# vim /etc/keepalived/keepalived.conf vrrp_instance VI_2 { interface eth0 state BACKUP # BACKUP for slaverouters priority 100 # 100 for BACKUP virtual_router_id 58 garp_master_delay 1 authentication { auth_typePASS auth_pass password } track_interface { eth0 } virtual_ipaddress { 172.16.58.8/16 dev eth0 label eth0:1 } track_script { chk_nginx chk_mantaince_down } notify_master "/etc/keepalived/notify.sh master eth0:1" notify_backup "/etc/keepalived/notify.sh backup eth0:1" notify_fault "/etc/keepalived/notify.sh fault eth0:1" }
我在nginx2中修改的部分(圈住的部分)如图所示:
# servicekeepalived restart ###启动keepalived服务器 #ifconfig
2、查看HA1的IP地址配置:
查看HA2的IP地址配置:
3、测试:
4、模拟nginx1发生故障
# cd/etc/keepalived/ # touch down
查看HA2上IP地址配置:
5、测试一下:
如果我测试的结果中,172.16.58.7(vip1)和172.16.58.8(vip2)显示的结果一样,那我就操作成功了。