一、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"–y
# 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  ###解压

用RHEL6.4(64bit)Nginx+Keepalived实现Nginx进程的主从、双主高可用_第1张图片

       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、测试网页

用RHEL6.4(64bit)Nginx+Keepalived实现Nginx进程的主从、双主高可用_第2张图片

用RHEL6.4(64bit)Nginx+Keepalived实现Nginx进程的主从、双主高可用_第3张图片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    ###复制服务脚本

用RHEL6.4(64bit)Nginx+Keepalived实现Nginx进程的主从、双主高可用_第4张图片

# vim keepalived

用RHEL6.4(64bit)Nginx+Keepalived实现Nginx进程的主从、双主高可用_第5张图片

(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.
        #/ brd dev  scope  label 

(2)在nginx2上(从服务器)修改配置文件

这里只需要设置这么多就行了。

用RHEL6.4(64bit)Nginx+Keepalived实现Nginx进程的主从、双主高可用_第6张图片

       我这里自己设置的有脚本(实现健康检查),大家如果想配置,可使用下面这个脚本,如果没有这个脚本的话,我试验的时候还需要关闭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

用RHEL6.4(64bit)Nginx+Keepalived实现Nginx进程的主从、双主高可用_第7张图片

7、编辑并测试网页:

我们首先要查看一下配置文件,看一下我们的网页在什么地方,然后就创建网页,创建完成后,我们就重启服务器然后进行测试,看自己设置的网页是否有问题,然后在测试一下我用vip测试一下显示的页面,并以此判断我上面的设置是否正确。

# mkdir /etc/nginx/html
# vim/etc/nginx/html/index.html

nginx1.shuaige.com

nginx2.shuaige.com

# servicenginx restart

用RHEL6.4(64bit)Nginx+Keepalived实现Nginx进程的主从、双主高可用_第8张图片

用RHEL6.4(64bit)Nginx+Keepalived实现Nginx进程的主从、双主高可用_第9张图片

用RHEL6.4(64bit)Nginx+Keepalived实现Nginx进程的主从、双主高可用_第10张图片

这个时候我将nginx1(主服务器)给停掉,我们这里看看虚拟ip是否会飘到从服务器上,我们可以从查看nginx1和nginx2的地址配置信息中得到(如果结果不变,我们有要多查看几次),如果还不信我们就用测试网页来检验我的实验是否正确。

# touch /etc/keepalived/down   ###停止主服务器上的
# ifconfig

用RHEL6.4(64bit)Nginx+Keepalived实现Nginx进程的主从、双主高可用_第11张图片

用RHEL6.4(64bit)Nginx+Keepalived实现Nginx进程的主从、双主高可用_第12张图片用RHEL6.4(64bit)Nginx+Keepalived实现Nginx进程的主从、双主高可用_第13张图片

三、双主模式

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中修改的部分(圈住的部分)如图所示:

用RHEL6.4(64bit)Nginx+Keepalived实现Nginx进程的主从、双主高可用_第14张图片

# servicekeepalived restart   ###启动keepalived服务器
#ifconfig

2、查看HA1的IP地址配置:

用RHEL6.4(64bit)Nginx+Keepalived实现Nginx进程的主从、双主高可用_第15张图片

查看HA2的IP地址配置:

用RHEL6.4(64bit)Nginx+Keepalived实现Nginx进程的主从、双主高可用_第16张图片

3、测试:

用RHEL6.4(64bit)Nginx+Keepalived实现Nginx进程的主从、双主高可用_第17张图片

用RHEL6.4(64bit)Nginx+Keepalived实现Nginx进程的主从、双主高可用_第18张图片

4、模拟nginx1发生故障

# cd/etc/keepalived/
# touch down

查看HA2上IP地址配置:

用RHEL6.4(64bit)Nginx+Keepalived实现Nginx进程的主从、双主高可用_第19张图片

5、测试一下:

       如果我测试的结果中,172.16.58.7(vip1)和172.16.58.8(vip2)显示的结果一样,那我就操作成功了。

用RHEL6.4(64bit)Nginx+Keepalived实现Nginx进程的主从、双主高可用_第20张图片

用RHEL6.4(64bit)Nginx+Keepalived实现Nginx进程的主从、双主高可用_第21张图片