前言:本文記述了搭建一個小型web服務器集群的過程,由於篇幅所限,系統、軟件的安裝和基本配置我這里就省略了,只記敘關鍵配置和腳本內容。假如各位朋友想了解各軟件詳細配置建議查閱官方文檔。
一 需求分析:
1.整體需求:搭建一個高可用的網站服務器集群,能承受高並發請求,能抵御一般的網絡攻擊,任何一台服務器的退服不影響整個集群的運作,並且能對各服務器的運行情況作出實時監控。
2.詳細需求分析:
根據需求,計划根據以下拓撲搭建運行環境:
二 詳細功能描述:
1.前端服務器采用nginx實現反向代理和負載均衡,用keepalive實現HA。此部分由centos1和centos4實現,centos1作為主服務器,centos4作為熱備服務器。Nginx會根據不同的請求ip機會均等地把請求發送到后端兩台服務器,並且以ip hash的方式保持各個ip的會話。
2.后端服務器構建在centos2與centos3上,采用apache作為web發布軟件,mysql作為數據庫,測試網頁用Django來實現。兩台服務器的數據庫能夠自動同步。
3.作為熱備服務器,centos4在centos1沒有故障時並沒有業務流量,處於相對空閑的狀態,因此在centos4上配置nfs使它成為文件共享服務器,網站文件放在這台服務器上。
4.centos5作為監控服務器,運行nagios監控各服務器狀態。出現告警時通過告警通知管理員。另外centos5還作為saltstack的服務器,其他主機的軟件安裝、文件傳輸、命令運行等操作均通過saltstack批量實現。
三 總體部署描述:
1.各個服務器均安裝centos6.4 64位版本,采用cobbler實現批量自動安裝。
2.各個軟件均安裝最新的穩定版,centos自帶的軟件也要進行升級,例如python,Centos自帶的版本比較久,會影響Django的運行。
四 詳細部署描述:
1.nginx設置
Nginx在這里的作用主要是反向代理、作為用戶以及服務器之間的緩存、以及以負載均衡的方式把請求發送到后端兩台服務器。
采用編譯安裝的方式安裝nginx,具體過程就不在此記述了,不過為了更好地抵擋入侵,建議編譯前修改一下安裝文件,使入侵者難以查出nginx的版本號,修改安裝文件中的nginx.h,把相關字段修改如下:
#define NGINX_VERSION "1.0" #define NGINX_VER "webserver" NGINX_VERSION
Nginx的配置文件如下:
user www www; worker_processes 2; events { worker_connections 12800; use epoll; } http { include mime.types; #協助部分瀏覽器(如firefox)識別網頁文件的類型 default_type application/octet-stream; limit_conn_zone $binary_remote_addr zone=addr:10m; sendfile on;#指定nginx是否調用sendfile函數來輸出文件,能提高性能。 tcp_nopush on; tcp_nodelay on; keepalive_timeout 20; #客戶端連接保持活動的超時時間 send_timeout 20; #指定響應客戶端的超時時間 client_body_buffer_size 1k; #指定客戶端請求主體緩沖區大小 client_header_buffer_size 1k; #指定來自客戶端請求頭的headerbuffer大小 large_client_header_buffers 2 1k; #客戶端請求中較大的消息頭指定的緩存最大數量和大小 server_tokens off; #禁止在錯誤頁面上顯示nginx版本號 client_body_timeout 20; #設置客戶端請求主體讀取超時時間 client_header_timeout 20; #設置客戶端請求頭讀取超時時間 gzip on; upstream WebServers{ #指定負載均衡服務器 ip_hash; #以ip_hash的方式保持會話 server 10.0.0.3; #沒有設定權重,因此兩台后端服務器將機會均等地接受請求 server 10.0.0.2; } server { listen 80; server_name localhost; location / { proxy_pass http://WebServers; limit_conn addr 100; #指定每個ip最多只允許建立100個連接 limit_rate 500k; #每個ip最大帶寬是500k
proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504 http_404; } error_page 500 502 503 504 /50x.html; location = /50x.html { root html; } } }
因為后端測試網頁是用Django寫的,因此nginx就不用處理php等動態網頁的請求了。
設置完畢后,把nginx設置為開機啟動,可以用如下方式:
echo “/usr/local/nginx/sbin/nginx” >>/etc/rc.local
2.Keeplive設置
Keeplive的作用是在兩台服務器(centos1、centos4)之間,以VRRP協議實現HA。通過虛擬出一個Virtul IP(本例子中是192.168.48.138)來對外發布業務。兩台服務器中的任一台退服了,keeplive會自動把業務轉到另一台上。在這里centos1是主服務器,centos4是備用服務器。平時的數據流量只會通過centos1,只有centos1退服了,數據流量才會割接到centos4上。
centos1上的keepalive配置:
! Configuration File for keepalived global_defs { notification_email { test@test.com #指定告警郵箱,當發現對端服務器退服時發郵件警報 } notification_email_from Alexandre.Cassen@firewall.loc smtp_server 127.0.0.1 smtp_connect_timeout 30 router_id LVS_DEVEL } vrrp_instance VI_1 { state MASTER interface eth0 #綁定網卡接口 virtual_router_id 51 mcast_src_ip 192.168.48.139 #設置本機通過哪個ip發送vrrp包 priority 100 #設置優先級 advert_int 1 #檢查間隔,這里設置為1秒 authentication { auth_type PASS auth_pass 1111 } virtual_ipaddress { 192.168.48.138 } }
對於從服務器centos4,只需要把以上配置文件的部分內容修改即可:
state BACKUP mcast_src_ip 192.168.48.140 priority 90
同樣,配置好配置文件后,把keepalive添加到開機啟動中。
對於keepalive,由於是使用VRRP協議來檢查對端是否在線的,只要對端能ping通,那么keeplive就會認為對端在線。但也有一種情況是對端服務器沒有宕機,但nginx出錯關閉了,這種情況下keepalive仍然會判斷對端在線,不會把業務割接到備用服務器。因此我們需要在服務器上運行一個腳本,監控nginx進程的狀態。假如nginx進程關閉了,先嘗試重啟nginx,無法重啟時就關閉keepalive的進程,使業務割接到備用端。腳本如下:
#!/bin/bash #nginxStatus.sh while : do nginxStatus=`ps -C nginx --no-header |wc -l` if ((nginxStatus==0));then /usr/local/nginx/sbin/nginx
sleep 5 nginxStatus=`ps -C nginx --no-header |wc -l` if ((nginxStatus==0));then /etc/init.d/keepalived stop fi else #當判斷到主服務器的nginx已經重新啟動后,再啟動keepalive,使業務倒置回主 #服務器 keepaliveStatus=`ps -C keepalived --no-header |wc -l` if ((keepaliveStatus==0));then /etc/init.d/keepalived start fi fi sleep 5 done
把這個腳本放在centos1上,設置開機后於后台運行:
echo “nohup ~/nginxStatus.sh &”>>/etc/rc.local
由於腳本中的while會不斷循環下去來檢查進程運行情況,因此沒必要添加進計划任務。不過謹慎起見,防止腳本意外停止,也可以在計划任務中設置每30分鍾運行一次。
3.后端Web服務器設置
后端web服務器運行在centos2、centos3上,采用apache搭建,測試網頁使用Django編寫,網頁文件存放在共享文件服務器centos4上,分別掛載在本機的/var/www/html目錄。
用saltstack批量在兩台服務器上安裝apache、apache-devel、wsgi、Django、mysql以及升級python,過程省略。
部署好運行環境后,修改一下apache的配置文件(http.conf),把運行用戶名和組名改為apache,根目錄指向/var/www/html
接下來設置mysql,把兩台服務器的mysql設置互為主從,使得其中一台服務器的數據庫變更了,馬上同步到另一台,保證兩台服務器的數據一致。設置步驟如下:
首先設置centos2
打開centos2上mysql的配置文件my.cnf,作如下修改:
[mysqld] log-bin=MySQL-bin #開啟二進制日志,從服務器靠讀取主服務器的二進制日志來執行同步 server-id=1 #設置為1表明這是主服務器。 binlog-ignore-db=mysql binlog-ignore-db=information_schema # binlog-ignore-db用來指定忽略哪些數據庫記錄到二進制日志,這樣在同步的時候就會忽略掉這些數據庫。其中“mysql”數據庫用於記錄用戶信息,“information_schema”用於記錄整個數據庫各個庫和表的信息。
然后以root身份登陸mysql,執行下面命令:
grant replication slave on *.* to ‘test’@’10.0.0.3’ identified ‘test123’;
這命令的含義是,在10.0.0.3機器上建立一個test用戶,用戶密碼為test123,這個用戶擁有對centos2上所有數據庫的所有表同步到centos3(10.0.0.3)的權限。
然后執行:
show master status;
可以看到以下信息:
記錄好這個信息,然后以root身份登陸centos3的mysql,執行下面的命令(有一點需要注意,假如同步之前需要同步的數據庫不是空的話,需要先在數據庫上加上讀鎖,把主服務器的相關數據庫導入到從數據庫中):
change master to master_host='10.0.0.2', master_user='test', master_password='test123', master_log_file='MySQL-bin.000070', master_log_pos=106;
上面的master_log_file和master_log_pos的信息就是之前centos2上執行show master status命令后所顯示的信息。執行show slave status;命令,查看一下centos3是否已經成功成為從服務器:
可以看到centos3的mysql已經成為centos2(10.0.0.2)的從服務器了。至此centos3的mysql已經能主動同步centos2的數據了,我們只需要把以上操作在centos3上操作一次(先修改my.cnf,其中server-id要設為2,然后再執行創建用戶以及授權命令),就能實現兩個數據庫互為主從,相互之間自動同步。
4.共享文件服務器nfs
本環節中,我們需要把centos4配置成共享文件的服務器端,后端兩台web服務器的網頁內容將放在共享文件服務器上,以此保證兩台web服務器的網頁文件內容一致。
nfs在centos6.2上默認已安裝,我們首先在centos4的/etc目錄下新建exports文件,加入以下內容:
/home/apache/html 10.0.0.2(rw,sync) #sync是指數據同時寫入緩存和硬盤中,執行較慢但保證數據精確 /home/apache/html 10.0.0.3(rw,sync)
然后在centos4上一步步執行下面的命令:
groupadd apache useradd -g apache -s /sbin/nologin apache passwd apache mkdir -p /home/apache/html #把網頁文件復制到/home/apache/html,然后再執行下面命令: chown -R apache:apache /home/apache/html chmod -R 700 /home/apache/html chkconfig --level 35 nfs on service nfs start
關於共享目錄的權限設置,由於后端兩台web服務器的apache都是以apache:apache這個用戶來執行的,文件的讀寫也是通過這個用戶,因此為保證數據安全,共享目錄只開放權限給這個用戶即可,其他用戶一律什么權限都不給予。另外還要把共享文件夾的所屬者設為apache:apache這個用戶。
接下來到centos2、centos3兩台web服務器上作如下設置,使它們開機時自動加載共享目錄:
echo "mount -t nfs 10.0.0.4:/home/apache/html /var/www/html -o hard,bg,nfsvers=3 ">>/etc/rc.local
上面選項中hard表示網絡短暫中斷時會繼續嘗試連接服務器,並且不會顯示錯誤信息;
bg表示執行mount時如果無法順利mount上時,系統會將mount的操作轉移到后台並繼續嘗試mount,直到mount成功為止;
nfsvers=3表示采用第3版的nfs。
5.nagios設置
監控軟件安裝在centos5上,負責監控其他4台服務器的情況,出現異常時發出郵件給管理員進行警報。
安裝過程:需要在centos5上安裝nagios、nagios插件包、nrpe、apache(用於搭建監控網頁)、pnp(用於生成監控數據的分析圖表)。4台被監控的主機需要安裝nagios插件包以及nrpe,詳細安裝過程這里省略。
以下項目是對於4台服務器都需要監控的:
1.Check Swap:監控交換分區的剩余空間
2.Check Zombie Procs:監控僵屍進程的數目
3.Total Processes:監控總進程的數目
4.check-no_alowed_user:監控是否有非允許的用戶登陸
5.check-system-load:監控系統負載
對於nagios的工作原理,簡單來說就是:在服務端的nagios目錄下的services.cfg文件上定義需要監控各個客戶端的哪個監控項目,對應的監控腳本被放在客戶端執行,執行結果通過客戶端上的nrpe守護進程反饋給nagios服務器端。
默認情況下監控腳本返回的值所代表的的含義如下:
OK—退出代碼 0—表示服務正常地工作。
WARNING—退出代碼 1—表示服務處於警告狀態。
CRITICAL—退出代碼 2—表示服務處於危險狀態。
UNKNOWN—退出代碼 3—表示服務處於未知狀態。
因此,要實現上述5個監控項目,首先修改nagios服務器端(即centos5)上的services.cfg文件,添加上以下內容:
define service{ use local-service host_name centos1 service_description check-no_alowed_user check_command check_nrpe!check_no_allowed_user #對於腳本在客戶端執行的命令,都要在命令前加上check_nrpe! notifications_enabled 1 #開啟告警功能 flap_detection_enabled 0 #關閉抖動檢測(當數據出現較大抖動時nagios不會作出提示) notification_options w,c,r #warning、critical、recover時發出告警 notification_interval 5 #假如服務的狀態沒有恢復正常的話,告警將每5分鍾發一次 notification_period 24x7 #設定告警的時段,這里設置為24X7 } define service{ use local-service,services-pnp #對於添加上services-pnp的服務,nagios將調用pnp把服務返回的數據畫成圖表。 host_name centos1 service_description check-system-load check_command check_nrpe!check_load notifications_enabled 1 flap_detection_enabled 0 notification_options w,c,r notification_interval 5 notification_period 24x7 } define service{ use local-service,services-pnp host_name centos1 service_description Total Processes check_command check_nrpe!check_total_procs notifications_enabled 1 flap_detection_enabled 0 notification_options w,c,r notification_interval 5 notification_period 24x7 } define service{ use local-service,services-pnp host_name centos1 service_description Check Zombie Procs check_command check_nrpe!check_zombie_procs notifications_enabled 1 flap_detection_enabled 0 notification_options w,c,r notification_interval 5 notification_period 24x7 } define service{ use local-service,services-pnp host_name centos1 service_description Check Swap check_command check_nrpe!check_swap notifications_enabled 1 flap_detection_enabled 0 notification_options w,c,r notification_interval 5 notification_period 24x7 }
以上寫出了描述centos1監控服務的描述,其他3台服務器的描述都是一樣的,只是host_name不一樣,限於篇幅,這里就不把剩余部分列出了。
接下來修改客戶端的設置,同樣以centos1為例,修改nagios目錄下的nrpe.cfg文件,添加以下內容(部分內容默認已經存在的,修改的時候需要注意有沒有重復):
command[check_swap]=/usr/local/nagios/libexec/check_swap -w 20% -c 10% #-w后面的參數告訴腳本當交換空間小於20%時,返回warming狀態(即返回1),-c后面的參數告訴腳本當交換空間小於10%時,返回critical狀態(即返回2) command[check_load]=/usr/local/nagios/libexec/check_load -w 1.8,1.5,1.2 -c 2.5,2,1.8 command[check_zombie_procs]=/usr/local/nagios/libexec/check_procs -w 5 -c 10 -s Z command[check_total_procs]=/usr/local/nagios/libexec/check_procs -w 150 -c 200 command[check_no_allowed_user]=/usr/local/nagios/libexec/check_no_allowed_user.py –a cjyfff
上面這5個命令對應的腳本中,check_no_allowed_user.py是我編寫的腳本,另外4個是nagios自帶的。check_no_allowed_user.py的作用是檢測是否有允許用戶以外的用戶登錄系統。使用方法是腳本后面加上選項-a,然后添加允許登錄的用戶列表(格式是-a user1,user2...,root默認已添加到允許列表,因此無需添加root)。當允許用戶列表以外的用戶登錄時,將會觸發nagios的critial告警。這個腳本的內容如下:
#! /usr/bin/env python #coding=utf-8 #check_no_allowed_user.py import os, sys, getopt def Usage(): print "Usage:\n\tpython check_no_allowed_user.py [-h|--help][-a|--add allowedUser01,allowUser02...]\n" print "\tTo creat the allowed user list,please use this format:-a user1,user2...." print "\tAny user(s) not in allowed user list login this system will alter Nagios." sys.exit(3) def CheckUser(args): a = os.popen("who").read() #這里a被賦值成一個包含換行符的字符串 #下面需要以換行符分隔把a建成列表,然后提取列表元素的第一個字段,這才是 #我們所需要的當前用戶名 b = list(a.split("\n")) c = [] i = 0 while i<len(b)-1: c.append(b[i].split(" ")[0]) i += 1 userList = set(c) allowedList = list(args.split(",")) #創建允許用戶的列表 allowedList.append("root") #允許用戶的列表中默認添加上root #print allowedList rs = [user for user in userList if user not in allowedList] #查出在當前用戶列表中但不在允許列表中的用戶 #print rs if rs : print "Detected NO ALLOWED user(s)%s"%rs sys.exit(2) else: print "All user is ALLOWED." sys.exit(0) try: options, args = getopt.getopt(sys.argv[1:], "ha:", ["help", "add="]) except getopt.GetoptError: Usage() for o,r in options: if o in ("-h", "--help"): Usage() if o in ("-a", "--add"): #print type(r) CheckUser(r)
另外,對於centos1、centos4兩台前端服務器,還需要添加2項監控服務:
80port:監控80端口的情況
CheckNginxState:監控nginx進程是否啟動
同樣,以centos1為例,修改centos5上的services.cfg,添加上以下內容:
define service{ use local-service,services-pnp host_name centos1 service_description 80port check_command check_tcp!80 #這個命令是通過服務器端的腳本來監控客戶端的80端口的,因此無需加上check_nrpe!,客戶端也不需要存在這個命令對應的腳本。 } define service{ use local-service host_name centos1 service_description CheckNginxState check_command check_nrpe!check_nginx }
然后修改centos1上的nrpe.cfg,添加上以下內容:
command[check_nginx]=/usr/local/nagios/libexec/check_nginx.sh
這里的check_nginx.sh也是我自己編寫的腳本,腳本內容如下:
#!/bin/bash #check_nginx.sh use_age="This script is for checking nginx status,if nginx is stop Nagios will alarm." if (($#!=0));then echo $use_age exit 3 fi a=`ps -C nginx --no-header |wc -l` if ((a!=0));then echo "nginx is running." exit 0 else echo "nginx is NOT running." exit 2 fi
對於centos4這台共享文件服務器,還需要增加一個監控項目,監控硬盤的容量大小,這里可以用nagios自帶的check_disk腳本,方法和上面一樣的,這里就不在敘述了。
對於作為后端web服務器的centos2、centos3,也很有必要監控服務器上apache以及mysql是否正在運行。監控腳本很簡單,只需要把上面check_nginx.sh中“a=`ps -C nginx --no-header |wc -l`”修改一下,把“nginx”替換為“httpd”和“mysqld”即可。
最后,客戶端都設置好監控設置了,作為服務器端,也可以為centos5增加監控內容,服務器端的監控腳本同樣放在服務器端的libexec/下,監控服務是在localhost.cfg中定義的。
謹記添加自己寫的腳本后要把用戶改為nagios:nagios,並且增加執行權限。
最終配置完畢后,打開nagios監控頁面的效果圖如下:
6、安全設置
為了減少ssh密碼被暴力破解的風險,在各個服務器上把ssh端口從默認的22端口更改為其他端口(本例子中改為2002),並且在/etc/hosts.allow中指定允許sshd通信的ip,在/etc/hosts.deny中添加sshd:ALL
考慮到服務器都是在內網環境,已經能隔絕外網的很多攻擊,並且開啟防火牆的話會影響服務器之間數據轉發速度,因此我在這4台服務器上都關閉iptables,僅在centos5上開啟。在實際生產環境中,依靠在集群前面的防火牆對集群進行進一步保護。
centos5的iptables腳本如下:
!#/bin/bash #centos5-iptables.sh iptables -F iptables -X iptables -Z iptables -P INPUT DROP iptables -P FORWARD ACCEPT iptables -P OUTPUT ACCEPT iptables -A INPUT -i lo -j ACCEPT iptables -A OUTPUT -o lo -j ACCEPT iptables -A INPUT -p icmp -j ACCEPT#允許icmp協議包通過 iptables -A INPUT -s 192.168.48.139 -j ACCEPT iptables -A INPUT -s 192.168.48.140 -j ACCEPT iptables -A INPUT -s 10.0.0.2 -j ACCEPT iptables -A INPUT -s 10.0.0.3 -j ACCEPT iptables -A INPUT -p tcp -m multiport --dport 22,80 -j ACCEPT /etc/rc.d/init.d/iptables save
五 結語:
最后我用apache benchmark測試了一下網站。雖然是在虛擬機環境,測試數據沒多大參考價值,但是通過測試也可以看看在高並發的環境下,那些服務器負荷最大。經測試,在100並發的環境下,雖然網頁還能打開,但是nagios已經失去了后端兩台web服務器的響應,提示request timeout了,其他兩台服務器的負載還是很低,說明在高並發的環境下,后端的web服務器負荷最大,也是重點需要優化的對象。
這個實驗環境存在一個不足,那就是共享文件只放在一台服務器上,容易造成單邊,建議條件許可的情況下可使用分布式存儲系統,例如MFS。
另外在搭建實驗環境的過程中還有2點待日后跟進:
1、優化apache的性能,讓后端服務器能應付更大並發。
2、Mysql的主從同步存在延遲問題,可能會導致主、從數據庫不一致。這個查看網上資料可以通過插件來解決,下一步嘗試接觸這些插件的使用方法。