双机高可用、负载均衡、MySQL(读写分离、主从自动切换)架构设计

架构简介

 

前几天网友来信说帮忙实现这样一个架构:只有两台机器,需要实现其中一台死机之后另一台能接管这台机器的服务,并且在两台机器正常服务时,两台机器都能用上。于是设计了如下的架构。
高可用/集群
此架构主要是由keepalived实现双机高可用,维护了一个外网VIP,一个内网VIP。正常情况时,外网VIP和内网VIP都绑定在server1服务器,web请求发送到server1的nginx,nginx对于静态资源请求就直接在本机检索并返回,对于php的动态请求,则负载均衡到server1和server2。对于SQL请求,会将此类请求发送到Atlas MySQL中间件,Atlas接收到请求之后,把涉及写操作的请求发送到内网VIP,读请求操作发送到mysql从,这样就实现了读写分离。

 

当主服务器server1宕机时,keepalived检测到后,立即把外网VIP和内网VIP绑定到server2,并把server2的mysql切换成主库。此时由于外网VIP已经转移到了server2,web请求将发送给server2的nginx。nginx检测到server1宕机,不再把请求转发到server1的php-fpm。之后的sql请求照常发送给本地的atlas,atlas把写操作发送给内网VIP,读操作发送给mysql从,由于内网VIP已经绑定到server2了,server2的mysql同时接受写操作和读操作。

 

当主服务器server1恢复后,server1的mysql自动设置为从,与server2的mysql主同步。keepalived不抢占server2的VIP,继续正常服务。

 

架构要求

 

要实现此架构,需要三个条件:

  • 1、服务器可以设置内网IP,并且设置的内网IP互通;
  • 2、服务器可以随意绑定IDC分配给我们使用的外网IP,即外网IP没有绑定MAC地址;
  • 3、MySQL服务器支持GTID,即MySQL-5.6.5以上版本。

 

环境说明

 

server1

  • eth0: 10.96.153.110(对外IP)
  • eth1: 192.168.1.100(对内IP)

server2

  • eth0: 10.96.153.114(对外IP)
  • eth1: 192.168.1.101(对内IP)

系统都是CentOS-6。

 

对外VIP: 10.96.153.239
对内VIP: 192.168.1.150

 

hosts设置

 

/etc/hosts:
192.168.1.100 server1
192.168.1.101 server2

 

Nginx PHP MySQL Memcached安装

 

这几个软件的安装推荐使用EZHTTP来完成。

 

解决session共享问题

 

php默认的session存储是在/tmp目录下,现在我们是用两台服务器作php请求的负载,这样会造成session分布在两台服务器的/tmp目录下,导致依赖于session的功能不正常。我们可以使用memcached来解决此问题。
上一步我们已经安装好了memcached,现在只需要配置php.ini来使用memcached,配置如下,打开php.ini配置文件,修改为如下两行的值:

  1. session.save_handler = memcache
  2. session.save_path = "tcp://192.168.1.100:11211,tcp://192.168.1.101:11211"

之后重启php-fpm生效。

 

Nginx配置

 

Server1配置

  1. http {
  2. […]
  3.     upstream php-server {
  4.            server 192.168.1.101:9000;
  5.            server 127.0.0.1:9000;
  6.            keepalive 100;
  7.     }
  8. […]
  9.  server {
  10.     […]
  11.         location ~ .php$ {
  12.                         fastcgi_pass   php-server;
  13.                         fastcgi_index  index.php;
  14.                         fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
  15.                         include        fastcgi_params;
  16.         }
  17.     […]
  18.  }
  19. […]
  20. }

 

Server2配置

  1. http {
  2. […]
  3.     upstream php-server {
  4.            server 192.168.1.100:9000;
  5.            server 127.0.0.1:9000;
  6.            keepalive 100;
  7.     }
  8. […]
  9.  server {
  10.     […]
  11.         location ~ .php$ {
  12.                         fastcgi_pass   php-server;
  13.                         fastcgi_index  index.php;
  14.                         fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
  15.                         include        fastcgi_params;
  16.         }
  17.     […]
  18.  }
  19. […]
  20. }

这两个配置主要的作用是设置php请求的负载均衡。

 

MySQL配置

 

mysql util安装

我们需要安装mysql util里的主从配置工具来实现主从切换。

  1. cd /tmp
  2. wget http://dev.mysql.com/get/Downloads/MySQLGUITools/mysql-utilities-1.5.3.tar.gz
  3. tar xzf mysql-utilities-1.5.3.tar.gz
  4. cd mysql-utilities-1.5.3
  5. python setup.py build
  6. python setup.py install

 

mysql my.cnf配置

server1:

  1. [mysql]
  2. […]
  3. protocol=tcp
  4. […]
  5. […]
  6. [mysqld]
  7. […]
  8. # BINARY LOGGING #
  9. log-bin = /usr/local/mysql/data/mysql-bin
  10. expire-logs-days = 14
  11. binlog-format= row
  12. log-slave-updates=true
  13. gtid-mode=on
  14. enforce-gtid-consistency =true
  15. master-info-repository=TABLE
  16. relay-log-info-repository=TABLE
  17. server-id=1
  18. report-host=server1
  19. report-port=3306
  20. […]

server2:

  1. [mysql]
  2. […]
  3. protocol=tcp
  4. […]
  5. [mysqld]
  6. […]
  7. # BINARY LOGGING #
  8. log-bin = /usr/local/mysql/data/mysql-bin
  9. expire-logs-days = 14
  10. binlog-format= row
  11. log-slave-updates=true
  12. gtid-mode=on
  13. enforce-gtid-consistency =true
  14. master-info-repository=TABLE
  15. relay-log-info-repository=TABLE
  16. server-id=2
  17. report-host=server2
  18. report-port=3306
  19. […]

这两个配置主要是设置了binlog和启用gtid-mode,并且需要设置不同的server-id和report-host。

 

开放root帐号远程权限

我们需要在两台mysql服务器设置root帐号远程访问权限。

  1. mysql> grant all on *.* to ‘root’@’192.168.1.%’ identified by ‘Xp29at5F37’ with grant option;
  2. mysql> grant all on *.* to ‘root’@’server1’ identified by ‘Xp29at5F37’ with grant option;
  3. mysql> grant all on *.* to ‘root’@’server2’ identified by ‘Xp29at5F37’ with grant option;
  4. mysql> flush privileges;

 

设置mysql主从

在任意一台执行如下命令:

  1. mysqlreplicate –master=root:Xp29at5F37@server1:3306 –slave=root:Xp29at5F37@server2:3306 –rpl-user=rpl:o67DhtaW

# master on server1: … connected.
# slave on server2: … connected.
# Checking for binary logging on master…
# Setting up replication…
# …done.

 

显示主从关系

  1. mysqlrplshow –master=root:Xp29at5F37@server1 –discover-slaves-login=root:Xp29at5F37

# master on server1: … connected.
# Finding slaves for master: server1:3306

# Replication Topology Graph
server1:3306 (MASTER)
|
+— server2:3306 – (SLAVE)

 

检查主从状态

  1. mysqlrplcheck –master=root:Xp29at5F37@server1 –slave=root:Xp29at5F37@server2

# master on server1: … connected.
# slave on server2: … connected.
Test Description Status
—————————————————————————
Checking for binary logging on master [pass]
Are there binlog exceptions? [pass]
Replication user exists? [pass]
Checking server_id values [pass]
Checking server_uuid values [pass]
Is slave connected to master? [pass]
Check master information file [pass]
Checking InnoDB compatibility [pass]
Checking storage engines compatibility [pass]
Checking lower_case_table_names settings [pass]
Checking slave delay (seconds behind master) [pass]
# …done.

 

Keepalived配置

 

keepalived安装(两台都装)

  1. yum -y install keepalived
  2. chkconfig keepalived on

 

keepalived配置(server1)

  1. vi /etc/keepalived/keepalived.conf
  1. vrrp_sync_group VG_1 {
  2. group {
  3. inside_network
  4. outside_network
  5. }
  6. }
  7.  
  8. vrrp_instance inside_network {
  9. state BACKUP
  10. interface eth1
  11. virtual_router_id 51
  12. priority 101
  13. advert_int 1
  14. authentication {
  15. auth_type PASS
  16. auth_pass 3489
  17. }
  18. virtual_ipaddress {
  19. 192.168.1.150/24
  20. }
  21. nopreempt
  22. notify /data/sh/mysqlfailover-server1.sh
  23. }
  24.  
  25. vrrp_instance outside_network {
  26. state BACKUP
  27. interface eth0
  28. virtual_router_id 50
  29. priority 101
  30. advert_int 1
  31. authentication {
  32. auth_type PASS
  33. auth_pass 3489
  34. }
  35. virtual_ipaddress {
  36. 10.96.153.239/24
  37. }
  38. nopreempt
  39. }

 

keepalived配置(server2)

  1. vrrp_sync_group VG_1 {
  2. group {
  3. inside_network
  4. outside_network
  5. }
  6. }
  7.  
  8. vrrp_instance inside_network {
  9. state BACKUP
  10. interface eth1
  11. virtual_router_id 51
  12. priority 100
  13. advert_int 1
  14. authentication {
  15. auth_type PASS
  16. auth_pass 3489
  17. }
  18. virtual_ipaddress {
  19. 192.168.1.150
  20. }
  21. notify /data/sh/mysqlfailover-server2.sh
  22. }
  23.  
  24. vrrp_instance outside_network {
  25. state BACKUP
  26. interface eth0
  27. virtual_router_id 50
  28. priority 100
  29. advert_int 1
  30. authentication {
  31. auth_type PASS
  32. auth_pass 3489
  33. }
  34. virtual_ipaddress {
  35. 10.96.153.239/24
  36. }
  37. }

此keepalived配置需要注意的是:

  • 1、两台server的state都设置为backup,server1增加nopreempt配置,并且server1 priority比server2高,这样用来实现当server1从宕机恢复时,不抢占VIP;
  • 2、server1设置notify /data/sh/mysqlfailover-server1.sh,server2设置notify /data/sh/mysqlfailover-server2.sh,作用是自动切换主从

/data/sh/mysqlfailover-server1.sh脚本内容:

  1. #!/bin/bash
  2.  
  3. sleep 10
  4. state=$3
  5. result=`mysql -h127.0.0.1 -P3306 -uroot -pXp29at5F37 -e ‘show slave status;’`
  6. [[ "$result" == "" ]] && mysqlState="master" || mysqlState="slave"
  7.  
  8. if [[ "$state" == "MASTER" ]];then
  9.   if [[ "$mysqlState" == "slave" ]];then
  10.     mysqlrpladmin –slave=root:Xp29at5F37@server1:3306 failover
  11.   fi
  12.  
  13. elif [[ "$state" == "BACKUP" ]];then
  14.   if [[ "$mysqlState" == "master" ]];then
  15.     mysqlreplicate –master=root:Xp29at5F37@server2:3306 –slave=root:Xp29at5F37@server1:3306 –rpl-user=rpl:o67DhtaW
  16.   fi
  17. fi
  18.  
  19. sed -i ‘s/proxy-read-only-backend-addresses.*/proxy-read-only-backend-addresses = 192.168.1.150:3306/’ /usr/local/mysql-proxy/conf/my.cnf
  20. mysql -h127.0.0.1 -P2345 -uuser -ppwd -e "REMOVE BACKEND 2;"

/data/sh/mysqlfailover-server2.sh脚本内容:

  1. #!/bin/bash
  2.  
  3. sleep 10
  4. state=$3
  5. result=`mysql -h127.0.0.1 -P3306 -uroot -pXp29at5F37 -e ‘show slave status;’`
  6. [[ "$result" == "" ]] && mysqlState="master" || mysqlState="slave"
  7.  
  8. if [[ "$state" == "MASTER" ]];then
  9.   if [[ "$mysqlState" == "slave" ]];then
  10.     mysqlrpladmin –slave=root:Xp29at5F37@server2:3306 failover
  11.   fi
  12.  
  13. elif [[ "$state" == "BACKUP" ]];then
  14.   if [[ "$mysqlState" == "master" ]];then
  15.     mysqlreplicate –master=root:Xp29at5F37@server1:3306 –slave=root:Xp29at5F37@server2:3306 –rpl-user=rpl:o67DhtaW
  16.   fi
  17. fi
  18.  
  19. sed -i ‘s/proxy-read-only-backend-addresses.*/proxy-read-only-backend-addresses = 192.168.1.150:3306/’ /usr/local/mysql-proxy/conf/my.cnf
  20. mysql -h127.0.0.1 -P2345 -uuser -ppwd -e "REMOVE BACKEND 2;"

 

Atlas设置

 

atlas安装

到这里下载最新版本,https://github.com/Qihoo360/Atlas/releases

  1. cd /tmp
  2. wget https://github.com/Qihoo360/Atlas/releases/download/2.2.1/Atlas-2.2.1.el6.x86_64.rpm
  3. rpm -i Atlas-2.2.1.el6.x86_64.rpm

 

atlas配置

  1. cd /usr/local/mysql-proxy/conf
  2. cp test.cnf my.cnf
  3. vi my.cnf

调整如下参数,

  1. proxy-backend-addresses = 192.168.1.150:3306
  2. proxy-read-only-backend-addresses = 192.168.1.101:3306
  3. pwds = root:qtyU1btXOo074Itvx0UR9Q==
  4. event-threads = 8

注意:
proxy-backend-addresse设置为内网VIP
proxy-read-only-backend-addresses设置为server2的IP
root:qtyU1btXOo074Itvx0UR9Q==设置数据库的用户和密码,密码是通过/usr/local/mysql-proxy/bin/encrypt Xp29at5F37生成。
更详细参数解释请查看,Atlas配置详解

 

启动atlas

  1. /usr/local/mysql-proxy/bin/mysql-proxy –defaults-file=/usr/local/mysql-proxy/conf/my.cnf

之后程序里配置mysql就配置127.0.0.1:1234就好。

 

部署atlas自动维护脚本

在两台机器都部署此脚本,并添加定时任务(如每2分钟运行一次)我们把脚本放在/data/sh/auto_maintain_atlas.sh,脚本内容为:

  1. #!/bin/bash
  2.  
  3. count=`mysql -N -h127.0.0.1 -P2345 -uuser -ppwd -e "select * from backends;" | wc -l`
  4.  
  5. if [[ "$count" == "1" ]];then
  6.   result=`mysql -hserver1 -P3306 -uroot -pXp29at5F37 -e ‘show slave statusG’`
  7.   if echo "$result" | grep Slave_IO_State;then
  8.     slaveIP=192.168.1.100
  9.   else
  10.     result=`mysql -hserver2 -P3306 -uroot -pXp29at5F37 -e ‘show slave statusG’`
  11.     slaveIP=192.168.1.101
  12.   fi
  13.  
  14.         slaveIORunning=`echo "$result" | awk -F’:’ ‘/Slave_IO_Running:/{print $2}’`
  15.         slaveSQLRunning=`echo "$result" | awk -F’:’ ‘/Slave_SQL_Running:/{print $2}’`
  16.         SlaveSQLRunning_State=`echo "$result" | awk -F’:’ ‘/Slave_SQL_Running_State:/{print $2}’`
  17.     
  18.   if [[ "$slaveIORunning" =~ "Yes" && "$slaveSQLRunning" =~ "Yes" && "$SlaveSQLRunning_State" =~ "Slave has read all relay log" ]];then
  19.     mysql -h127.0.0.1 -P2345 -uuser -ppwd -e "add slave ${slaveIP}:3306;"
  20.   fi
  21. fi

为什么需要这个脚本呢?假设目前mysql主服务器在s1,s1宕机后,s2接管VIP,接着删除atlas中设置的slave backend,其mysql提升为主。过一段时间后,s1从宕机中恢复,这时候s1的mysql自动切换为从,接着删除atlas中设置的slave backend,开始连接s2的mysql主同步数据。到这个时候我们发现,已经不存在读写分离了,所有的sql都发送给了s2的mysql。auto_maintain_atlas.sh脚本就派上用场了,此脚本会定时的检查主从是否已经同步完成,如果完成就自动增加slave backend,这样读写分离又恢复了,完全不需要人工干预。

 

server1主宕机测试

 

测试keepalived是否工作正常

我们来模拟server1宕机。
在server1上执行shutdown关机命令。
此时我们登录server2,执行ip addr命令,输出如下:
1: lo: mtu 16436 qdisc noqueue state UNKNOWN
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: eth0: mtu 1500 qdisc pfifo_fast state UP qlen 1000
link/ether 00:0c:29:81:9d:42 brd ff:ff:ff:ff:ff:ff
inet 10.96.153.114/24 brd 10.96.153.255 scope global eth0
inet 10.96.153.239/24 scope global secondary eth0
inet6 fe80::20c:29ff:fe81:9d42/64 scope link
valid_lft forever preferred_lft forever
3: eth1: mtu 1500 qdisc pfifo_fast state UP qlen 1000
link/ether 00:0c:29:81:9d:4c brd ff:ff:ff:ff:ff:ff
inet 192.168.1.101/24 brd 192.168.1.255 scope global eth1
inet 192.168.1.150/32 scope global eth1
inet6 fe80::20c:29ff:fe81:9d4c/64 scope link
valid_lft forever preferred_lft forever

我们看到对外VIP 10.96.153.239和对内IP 192.168.1.150已经转移到server2了,证明keepalived运行正常。

 

测试是否自动切换了主从

登录server2的mysql服务器,执行show slave status;命令,如下:
mysql> show slave statusG
Empty set (0.00 sec)

我们发现从状态已经为空,证明已经切换为主了。

 

测试server1是否抢占VIP

为什么要测试这个呢?如果server1恢复之后抢占了VIP,而我们的Atlas里后端设置的是VIP,这样server1启动之后,sql的写操作就会向server1的mysql发送,而server1的mysql数据是旧于server2的,所以这样会造成数据不一致,这个是非常重要的测试。
我们先来启动server1,之后执行ip addr,输出如下:
1: lo: mtu 16436 qdisc noqueue state UNKNOWN
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: eth0: mtu 1500 qdisc pfifo_fast state UP qlen 1000
link/ether 00:0c:29:f1:4f:4e brd ff:ff:ff:ff:ff:ff
inet 10.96.153.110/24 brd 10.96.153.255 scope global eth0
inet6 fe80::20c:29ff:fef1:4f4e/64 scope link
valid_lft forever preferred_lft forever
3: eth1: mtu 1500 qdisc pfifo_fast state UP qlen 1000
link/ether 00:0c:29:f1:4f:58 brd ff:ff:ff:ff:ff:ff
inet 192.168.1.100/24 brd 192.168.1.255 scope global eth1
inet6 fe80::20c:29ff:fef1:4f58/64 scope link
valid_lft forever preferred_lft forever

我们看到,server1并没有抢占VIP,测试正常。不过另人郁闷的是,在虚拟机的环境并没有测试成功,不知道为什么。

 

测试server2的atlas是否已经删除slave backend

我们测试这个是为了保证atlas已经没有slave backend,也就是没有从库的设置了,否则当server1恢复时,有可能会把读请求发送给server1的mysql,造成读取了旧数据的问题。

[root@server1 ~]# mysql -h127.0.0.1 -P2345 -uuser -ppwd
mysql> select * from backends;
+————-+——————–+——-+——+
| backend_ndx | address | state | type |
+————-+——————–+——-+——+
| 1 | 192.168.1.150:3306 | up | rw |
+————-+——————–+——-+——+
1 rows in set (0.00 sec)
如果看到只有一个后端,证明运作正常。

 

测试server1 mysql是否设置为从

serve1恢复后,登录server1的mysql服务器,执行show slave status;命令,如下:

mysql> show slave statusG
*************************** 1. row ***************************
Slave_IO_State: Opening tables
Master_Host: server1
Master_User: rpl
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: mysql-bin.000015
Read_Master_Log_Pos: 48405991
Relay_Log_File: mysql-relay-bin.000002
Relay_Log_Pos: 361
Relay_Master_Log_File: mysql-bin.000015
Slave_IO_Running: Yes
Slave_SQL_Running: yes

 

测试是否自动恢复读写分离

server1恢复后一段时间,我们可以看是读写分离是否已经恢复。

[root@server1 ~]# mysql -h127.0.0.1 -P2345 -uuser -ppwd
Warning: Using a password on the command line interface can be insecure.
Welcome to the MySQL monitor. Commands end with ; or g.
Your MySQL connection id is 1
Server version: 5.0.99-agent-admin

Copyright (c) 2000, 2014, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type ‘help;’ or ‘h’ for help. Type ‘c’ to clear the current input statement.

mysql> select * from backends;
+————-+——————–+——-+——+
| backend_ndx | address | state | type |
+————-+——————–+——-+——+
| 1 | 192.168.1.150:3306 | up | rw |
| 2 | 192.168.1.100:3306 | up | ro |
+————-+——————–+——-+——+
2 rows in set (0.00 sec)

我们看到server1已经被添加为slave backend了。这表示已经成功恢复读写分离。

代码发布系统实现

日常运维问题

 

在我日常运维工作中,代码发布可能是最普遍的一项工作之一,尤其是网页代码的更新,碎片化发布需求非常频繁。在前期开发人员比较少时,还可以由自己来上服务器通过脚本来发布代码。但随着公司项目的增多,更多的开发人员加入到公司,发布代码需求开始增多,这就占用了我大部分时间,经常的被打断其它工作来发布代码,非常地不爽,然后开始想解决方法。

 

尝试解决问题

 

当然,发布代码肯定是运维的职责之一了,但频繁的发布导致运维大部分时间浪费在重复的操作上,非常的不值得。基于此,开始限制代码发布频率,要求把不是很紧急的更新延后到一周中的几个时间点。但实施起来效果不理想,治标不治本,原因是你不能强制把需要立即上线的更改延后。实施这样的定时发布,有可能影响项目的快速迭代。

 

最终解决方案

 

想到这样子下去也不是办法,会造成工作很被动,于是开始着手建立以Web操作方式,结合git,rsync来实现自动代码发布。公司代码管理目前用的是svn,开发人员在发布前也没有打Tag的习惯,所以想到分布式的git来完成版本的管理,rsync当然是用来同步代码到其它服务器了。附上几张代码发布系统的截图:
运维自动化
运维自动化
运维自动化

 

开源技术使用

 

  • rsync:用来同步代码到服务器;
  • git: 用来标记版本,回滚版本;
  • tornado: python的一个web构架,提供后台服务;
  • angularjs: 前端的一个mvc框架,用来实现浏览器与后端的交互,使得后端不需要关心前端网页的渲染,专注后端逻辑的开发。前端和后端通过json数据来通信;
  • bootstrap: 让运维人员写的网站后台UI也可以很专业。

 

代码发布流程

 

运维自动化
从流程图可以看到,我们只需要把审核发布的权限交给开发组负责人,运维只需要维护系统的稳定,之后代码发布就不需要运维来参与了。

 

以上是整体的流程,现在来说详细说下具体的逻辑实现:

  • 1、开发人员提交代码更新,主要提交的字段包括“更新理由”,“svn代码路径”;
  • 2、后端收到请求后,把此数据插入到数据库,标记此更新单为“等待预发布环境更新”的状态;
  • 3、后台进程定时查询是否有等待预发布环境更新的更新单,如果有,读取svn路径,执行svn up更新代码操作,并标记此更新单为“预发布环境已更新,等待完成测试”;
  • 4、开发人员或者测试人员通过预发布环境的域名来测试功能是否正常,如果不正常,作代码修改后提交svn,再到web发布后台点击“返回修改”,对svn路径或者不做任何修改再点击“重新提交”,然后更新单又一次回到”等待预发布环境更新“状态。循环3、4步骤,直至预发布环境测试通过为止;
  • 5、在确认测试通过后,开发人员点击”测试通过“,这时更新单进入”等待审核状态“;
  • 6、负责人确认可以发布后,点击”审批“按钮,这时更新单进入”审核通过,等待执行发布操作“的状态。这时,开发人员得到发布代码的授权;
  • 7、开发人员点击”发布代码“按钮,更新单进入”已执行发布,等待系统完成发布“状态;
  • 8、后台进程查询状态为”已执行发布,等待系统完成发布“的更新单,执行git发布命令。git命令大概为,进入预发布代码目录,执行git add .;git commit -m “更新原因”;git tag 上一次版本号+1,再进入已发布代码的目录,执行git pull同步预发布代码目录的更改。最后调用rsync命令同步代码到生产环境。

 

下面是回滚流程:

  • 1、进入web代码发布系统,选择已发布的版本,点击“申请回滚”;
  • 2、负责人审核此次回滚;
  • 3、开发人员执行回滚操作;
  • 4、后台查询“等待回滚”的记录,假如回滚的版本号为18,进入已发布代码的目录,执行git checkout -b 18 18;git checkout 18(这两条git命令作用为,以tag 18创建分支号为18的分支,并切换当前分支为18),然后再通过rsync命令来同步代码到生产环境,这样就实现了版本的回滚。

 

最后想说的话

 

最后想说的是,运维工作可以是枯燥的,也可以是有趣的。枯燥是因为没有意识或者懒得把重复的操作通过制定流程来使其自动化,在不断地把各种在运维工作中占用时间比较多的重复操作通过技术来使得自动化时,我们既高效完成了工作,节省了时间,又能提高编程和解决问题的能力,只有这样,我们才能让运维工作变得既有趣又有挑战性。

HTTP/HTTPS自动加密上网方案

这里主要介绍电脑无需任何设置,就能够自动加密代理特定网站的HTTP/HTTPS协议。

 

方案介绍

 

涉及到的软件

  • BIND: 一个流行的域名解析服务器,我们可以设置哪些域名需要走加密线路。
  • Stunnel: 使用TLS对tcp协议进行加密,也就是对tcp建立一条加密线路。
  • SNI Proxy: 代理软件。对于HTTP协议,它可以根据Host请求头解析得出目标站IP;对于HTTPS协议,它可以根据SNI扩展中的域名解析得出目标站IP。

 

此方案优缺点

优点:
无需手动设置任何代理,就能够自动加密代理特定网站的HTTP或HTTPS协议
相对于我们常用的ssh隧道,ssh隧道是单路,而此方案是支持多并发连接,可以极大加速网站访问。

缺点:
对于代理HTTPS协议,需要发起HTTPS连接的客户端,比如浏览器支持TLS的SNI扩展。好消息是目前浏览器几乎都支持此扩展,但对于一些非浏览器的客户端,不支持SNI扩展。我们只能设置正向代理来解决此问题。

 

方案原理

流程图:
服务器安全

原理介绍:

  • 1、首先我们需要准备三台服务器,一台是内网DNS服务器(安装bind),一台是内网代理服务器(安装stunnel),另一台国外服务器(安装stunnel,sniproxy)。
  • 2、我们还需要设置DNS为内网的DNS,并在内网bind dns设置谷歌域名解析的IP为内网代理服务器
  • 3、当我们访问谷歌网站时,首先会向内网DNS服务器发送DNS A记录查询,此时内网DNS服务器会返回内网代理服务器的IP。
  • 4、浏览器得到谷歌域名的解析IP后(即内网代理服务器的IP),会向内网代理服务器发送HTTP或HTTPS请求。
  • 5、此时内网代理服务器(即stunnel),会接收到请求,经过加密,把请求转发到国外服务器(stunnel)的指定端口上。
  • 6、国外服务器(stunnel)接收到来自国内服务器(stunnel)的加密数据后,经过解密,把请求转发到sniproxy。
  • 7、sniproxy再根据HTTP Host请求头或者HTTPS sni扩展的域名解析出谷歌服务器的IP,并把请求转发给谷歌服务器。
  • 8、谷歌服务器收到来自sniproxy发送的请求后,马上返回网页内容给sniproxy,sniproxy再原路返回数据给浏览器。

 

方案实施

由于时间有限,我们仅在Ubuntu server 12.04演示安装。

环境介绍

  • 系统:Ubuntu server 12.04
  • 内网DNS IP: 10.96.153.201(主),10.96.153.204(从)
  • 内网代理服务器: 10.96.153.204
  • 国外服务器IP: 1.2.3.4

安装BIND9

1、在主DNS和从DNS安装bind,即10.96.153.201(主),10.96.153.204(从)。

  1. wget http://www.isc.org/downloads/file/bind-9-10-0b1-2/?version=tar.gz -O bind-9-10-0b1-2.tar.gz
  2. tar xzf bind-9-10-0b1-2.tar.gz
  3. cd bind-9-10-0b1-2
  4. ./configure –prefix=/usr/local/bind
  5. make && make install

2、配置主DNS服务器(10.96.153.201)
2.1、生成/usr/local/bind/etc/rndc.key密钥文件

  1. /usr/local/bind/sbin/rndc-confgen -a -k rndckey -c /usr/local/bind/etc/rndc.key

2.2、编辑/usr/local/bind/etc/named.conf,写入如何内容:

  1. include "/usr/local/bind/etc/rndc.key";
  2. controls { inet 127.0.0.1 port 953 allow { 127.0.0.1; } keys { "rndckey"; }; };
  3. logging {
  4. channel default_syslog { syslog local2; severity notice; };
  5. channel audit_log { file "/var/log/bind.log"; severity notice; print-time yes; };
  6. category default { default_syslog; };
  7. category general { default_syslog; };
  8. category security { audit_log; default_syslog; };
  9. category config { default_syslog; };
  10. category resolver { audit_log; };
  11. category xfer-in { audit_log; };
  12. category xfer-out { audit_log; };
  13. category notify { audit_log; };
  14. category client { audit_log; };
  15. category network { audit_log; };
  16. category update { audit_log; };
  17. category queries { audit_log; };
  18. category lame-servers { audit_log; };
  19. };
  20. options {
  21.     directory "/usr/local/bind/etc";
  22. pid-file "/usr/local/bind/var/run/bind.pid";
  23. transfer-format many-answers;
  24. interface-interval 0;
  25. forward only;
  26. forwarders { 202.96.128.166;202.96.134.133; };
  27. allow-query {any;};
  28. };
  29. zone "google.com" {
  30. type master;
  31. file "google.com.zone";
  32. allow-transfer { 10.96.153.204; };
  33. };

在这个named.conf文件中,我们只需要关心如下内容:
对于options{}区域,202.96.128.166和202.96.134.133这两个是ISP提供的本地DNS,需要修改为自己所在ISP的本地DNS。
对于zone “google.com”{}区域,这里定义了google.com域名的区域文件google.com.zone,还有允许10.96.153.204(即从DNS)同步区域文件。
2.3、建立google.com.zone区域文件

  1. $TTL 3600
  2. @ IN SOA ns1.google.com. hostmaster.google.com. (
  3. 2014072015  ; Serial
  4. 3600 ; Refresh
  5. 900 ; Retry
  6. 3600000 ; Expire
  7. 3600 ) ; Minimum
  8. @ IN NS ns1.google.com.
  9. @ IN NS ns2.google.com.
  10. ns1 IN A 10.96.153.201
  11. ns2 IN A 10.96.153.204
  12. @ IN A 10.96.153.204
  13. * IN A 10.96.153.204

对于这个区域文件,
ns1 IN A 10.96.153.201 指向第一个dns服务器,即主DNS。
ns2 IN A 10.96.153.204 指向第二个dns服务器,即从DNS。
@ IN A 10.96.153.204和* IN A 10.96.153.204指向内网的代理服务器(stunnel)。我们只需要修改这三个地方就好了。

3、配置从DNS服务器(10.96.153.204)
编辑named.conf,写入如下内容

  1. logging {
  2. channel default_syslog { syslog local2; severity notice; };
  3. channel audit_log { file "/var/log/bind.log"; severity notice; print-time yes; };
  4. category default { default_syslog; };
  5. category general { default_syslog; };
  6. category security { audit_log; default_syslog; };
  7. category config { default_syslog; };
  8. category resolver { audit_log; };
  9. category xfer-in { audit_log; };
  10. category xfer-out { audit_log; };
  11. category notify { audit_log; };
  12. category client { audit_log; };
  13. category network { audit_log; };
  14. category update { audit_log; };
  15. category queries { audit_log; };
  16. category lame-servers { audit_log; };
  17. };
  18. options {
  19.     directory "/usr/local/bind/etc";
  20. pid-file "/usr/local/bind/var/run/bind.pid";
  21. transfer-format many-answers;
  22. interface-interval 0;
  23. forward only;
  24. forwarders { 202.96.128.166;202.96.134.133; };
  25. allow-query {any;};
  26. };
  27.  
  28. zone "google.com" {
  29. type slave;
  30. file "google.com.zone";
  31. masters { 10.96.153.201; };
  32. };

配置从DNS就简单得多,只需要写入如上内容到named.conf文件。同样的,
options{}中202.96.128.166和202.96.134.133这两个是当地ISP本地dns。
zone “google.com”{}中10.96.153.201指明主DNS服务器IP。
4、启动bind dns服务器

  1. /usr/local/bind/sbin/named

安装Stunnel

1、在内网代理服务器和国外主机安装stunnel

  1. apt-get install stunnel4

2、内网代理服务器stunnel配置
编辑/etc/default/stunnel4,设置ENABLED=1。
编辑/etc/stunnel/stunnel.conf,内容如下:

  1. client = yes
  2. pid = /etc/stunnel/stunnel.pid
  3. [http]
  4. accept = 80
  5. connect = 1.2.3.4:8082
  6.  
  7. [https]
  8. accept = 443
  9. connect = 1.2.3.4:4433

此配置文件表示,监听了80端口,并把此端口流量转发到1.2.3.4:8082,监听了443端口,并把此端口流量转发到1.2.3.4:4433
3、国外服务器stunnel配置
3.1、生成ssl证书stunnel.pem文件

  1. openssl genrsa -out key.pem 2048
  2. openssl req -new -x509 -key key.pem -out cert.pem -days 1095
  3. cat key.pem cert.pem >> /etc/stunnel/stunnel.pem

3.2、编辑/etc/stunnel/stunnel.conf文件

  1. client = no
  2. [http]
  3. accept = 1.2.3.4:8082
  4. connect = 127.0.0.1:8082
  5. cert = /etc/stunnel/stunnel.pem
  6.  
  7. [https]
  8. accept = 1.2.3.4:4433
  9. connect = 127.0.0.1:4433
  10. cert = /etc/stunnel/stunnel.pem

此配置文件表示,监听了1.2.3.4:8082,并转发此地址流量到127.0.0.1:8082,监听了1.2.3.4:4433,并转发给地址流量到127.0.0.1:4433。
3.3、编辑/etc/default/stunnel4,设置ENABLED=1。
4、启动stunnel

  1. service stunnel4 start

安装sniproxy

sniproxy项目地址:https://github.com/dlundquist/sniproxy
1、安装sniproxy
同样只演示在ubuntu server 12.04安装。
1.1、安装UDNS

  1. mkdir udns_packaging
  2. cd udns_packaging
  3. wget http://archive.ubuntu.com/ubuntu/pool/universe/u/udns/udns_0.4-1.dsc
  4. wget http://archive.ubuntu.com/ubuntu/pool/universe/u/udns/udns_0.4.orig.tar.gz
  5. wget http://archive.ubuntu.com/ubuntu/pool/universe/u/udns/udns_0.4-1.debian.tar.gz
  6. tar xfz udns_0.4.orig.tar.gz
  7. cd udns-0.4/
  8. tar xfz ../udns_0.4-1.debian.tar.gz
  9. dpkg-buildpackage
  10. cd ..
  11. dpkg -i *.deb

1.2、安装sniproxy

  1. apt-get install autotools-dev cdbs debhelper dh-autoreconf dpkg-dev gettext libev-dev libpcre3-dev libudns-dev pkg-config
  2. wget https://github.com/dlundquist/sniproxy/archive/master.zip
  3. unzip master.zip
  4. cd sniproxy-master/
  5. dpkg-buildpackage
  6. cd ..
  7. dpkg -i *.deb

2、配置sniproxy
/etc/sniproxy.conf内容如下:

  1. user daemon
  2. pidfile /var/run/sniproxy.pid
  3. error_log {
  4.     syslog deamon
  5.     priority notice
  6. }
  7. listen 127.0.0.1:8082 {
  8.     proto http
  9.     table http_hosts
  10. }
  11. table http_hosts {
  12.         .*      *:80
  13. }
  14.  
  15. listen 127.0.0.1:4433 {
  16.     proto tls
  17.     table https_hosts
  18. }
  19. table https_hosts {
  20. .* *:443
  21. }

此配置文件表示,监听了127.0.0.1:8082地址,并解析http协议中的Host请求头为IP,然后转发请求到此IP;监听了127.0.0.1:4433地址,并解析TLS中SNI扩展中的域名为IP,并转发请求到此IP。
3、启动sniproxy

  1. sniproxy

 

结束

到目前为止,我们已经搭建完成了整套HTTP/HTTPS加密代理方案。方案中的HTTP明文协议,利用stunnel使用了TLS加密,变成了HTTPS协议,使得数据包无法被解析出明文。方案中的HTTPS协议,本身是加密的,但为了防止SNI扩展的中域名被嗅探,还是走了stunnel的加密通道。对于发送HTTPS请求而不支持SNI扩展的客户端,需要手动设置下代理。下一篇博文我们来介绍加密的正向代理方案。

Linux命令行抓包及包解析工具tshark(wireshark)使用实例解析

在Linux下,当我们需要抓取网络数据包分析时,通常是使用tcpdump抓取网络raw数据包存到一个文件,然后下载到本地使用wireshark界面网络分析工具进行网络包分析。
最近才发现,原来wireshark也提供有Linux命令行工具-tshark。tshark不仅有抓包的功能,还带了解析各种协议的能力。下面我们以两个实例来介绍tshark工具。

1、安装方法

  1. CentOS: yum install -y wireshark
  2. Ubuntu: apt-get install -y tshark

2、实时打印当前http请求的url(包括域名)

  1. tshark -s 512 -i eth0 -n -f ‘tcp dst port 80’ -R ‘http.host and http.request.uri’ -T fields -e http.host -e http.request.uri -l | tr -d ‘t’

下面介绍参数含义:

  • -s 512 :只抓取前512个字节数据
  • -i eth0 :捕获eth0网卡
  • -n :禁止网络对象名称解析
  • -f ‘tcp dst port 80’ :只捕捉协议为tcp,目的端口为80的数据包
  • -R ‘http.host and http.request.uri’ :过滤出http.host和http.request.uri
  • -T fields -e http.host -e http.request.uri :打印http.host和http.request.uri
  • -l :输出到标准输出

3、实时打印当前mysql查询语句

  1. tshark -s 512 -i eth0 -n -f ‘tcp dst port 3306’ -R ‘mysql.query’ -T fields -e mysql.query

下面介绍参数含义:

  • -s 512 :只抓取前512个字节数据
  • -i eth0 :捕获eth0网卡
  • -n :禁止网络对象名称解析
  • -f ‘tcp dst port 3306’ :只捕捉协议为tcp,目的端口为3306的数据包
  • -R ‘mysql.query’ :过滤出mysql.query
  • -T fields -e mysql.query :打印mysql查询语句

tshark使用-f来指定捕捉包过滤规则,规则与tcpdump一样,可以通过命令man pcap-filter来查得。
tshark使用-R来过滤已捕捉到的包,与界面版wireshark的左上角Filter一致。

借助tcpdump统计http请求

这里所说的统计http请求,是指统计QPS(每秒请求数),统计前十条被访问最多的url。一般做这样的统计时,我们经常会使用网站访问日志来统计。当我们来到一个陌生的服务器环境,需要立即统计当前前十条被访问最多的url,来初步确定是否存在攻击行为,使用tcpdump则简单得多,因为我们不需要关心网站日志在哪,不需要考虑网站日志有没有开启之类的问题,直接用tcpdump捕捉当前的http包,再进一步过滤,就会得出我们想要的统计。此功能已集成到EZHTTP,下面是效果图:
Shell
下面介绍其统计方法。
1、捕捉10秒的数据包。

  1. tcpdump -i eth0 tcp[20:2]=0x4745 or tcp[20:2]=0x504f -w /tmp/tcp.cap -s 512 2>&1 &
  2. sleep 10
  3. kill `ps aux | grep tcpdump | grep -v grep | awk ‘{print $2}’`

此命令表示监控网卡eth0,捕捉tcp,且21-22字节字符为GE或者PO,表示匹配GET或者POST请求的数据包,并写到/tmp/tcp.cap文件。
2、这时候我们得到最新10秒的二进制数据包文件,我们下一步就是通过strings命令来找出GET/POST的url以及Host。

  1. strings /tmp/tcp.cap | grep -E "GET /|POST /|Host:" | grep –no-group-separator -B 1 "Host:" | grep –no-group-separator -A 1 -E "GET /|POST /" | awk ‘{url=$2;getline;host=$2;printf ("%sn",host""url)}’ > url.txt

此命令是本文的关键,通过strings显示二进制文件tcp.cap所有可打印字符,然后通过grep和awk过滤出http请求,并把拼接得到的url(包括域名+uri)写进一个文件url.txt。
3、这时我们拿到了近10秒钟所有的访问url,接下来的统计就容易得出,比如:
统计QPS:

  1. (( qps=$(wc -l /tmp/url.txt | cut -d’ ‘ -f 1) / 10 ))

排除静态文件统计前10访问url:

  1. grep -v -i -E ".(gif|png|jpg|jpeg|ico|js|swf|css)" /tmp/url.txt | sort | uniq -c | sort -nr | head -n 10

网络分析shell脚本(实时流量+连接统计)

介绍一个强大的分析网络的shell脚本,此脚本是从EZHTTP拆分出来的,觉得有必要单独介绍下。
脚本运行效果截图:
Shell

Shell

Shell
此脚本包含的功能有:

  • 1、实时监控任意网卡的流量
  • 2、统计10秒内平均流量
  • 3、统计每个端口在10秒内的平均流量,基于客户端和服务端端口统计。可以看出哪些端口占流量比较大,对于web服务器,一般是80端口。其它端口受到攻击时,也有可能其它端口流量比较大。所以此功能可以帮助我们端口流量是否正常。
  • 4、统计在10s内占用带宽最大的前10个ip。此项功能可以帮助我们来查出是否有恶意占用带宽的ip。
  • 5、统计连接状态。此项功能可以让我们看出哪些连接状态比较大。如果SYN-RECV状态比较多的话,有可以受到半连接攻击。如果ESTABLISED非常大,但通过日志发现没有那么多请求,或者通过tcpdump发现大量ip只建立连接不请求数据的话,可能是受到了全连接攻击,这时候如果你使用的是nginx服务器,可以在配置文件增加listen 80 deferred来防止。
  • 6、统计各端口连接状态。当可能受到攻击时,此项功能可以帮助我们发现是哪个端口受到攻击。
  • 7、统计端口为80且状态为ESTAB连接数最多的前10个IP。此项功能可以帮助我们来找出创建连接过多的Ip,进而屏蔽。
  • 8、统计端口为80且状态为SYN-RECV连接数最多的前10个IP。当受到半连接攻击时,此项功能可以帮助我们找到恶意ip。

用到的网络分析工具:

  • 1、tcpdump:此脚本用tcpdump来统计基于ip或基于端口的流量。
  • 2、ss: 此脚本用ss命令来统计连接状态,实际使用发现ss比netstat高效得多。
  • 3、/proc/net/dev,用来统计指定网卡的流量。

脚本下载地址:http://devops.webres.wang/wp-content/uploads/2014/06/network-analysis.sh
下面贴出完整的脚本:

  1. #!/bin/bash
  2.  
  3. #write by zhumaohai(admin#webres.wang)
  4. #author blog: devops.webres.wang
  5.  
  6.  
  7. #显示菜单(单选)
  8. display_menu(){
  9. local soft=$1
  10. local prompt="which ${soft} you’d select: "
  11. eval local arr=(${${soft}_arr[@]})
  12. while true
  13. do
  14.     echo -e "#################### ${soft} setting ####################nn"
  15.     for ((i=1;i<=${#arr[@]};i++ )); do echo -e "$i) ${arr[$i-1]}"; done
  16.     echo
  17.     read -p "${prompt}" $soft
  18.     eval local select=$$soft
  19.     if [ "$select" == "" ] || [ "${arr[$soft-1]}" == ""  ];then
  20.         prompt="input errors,please input a number: "
  21.     else
  22.         eval $soft=${arr[$soft-1]}
  23.         eval echo "your selection: $$soft"             
  24.         break
  25.     fi
  26. done
  27. }
  28.  
  29. #把带宽bit单位转换为人类可读单位
  30. bit_to_human_readable(){
  31.     #input bit value
  32.     local trafficValue=$1
  33.  
  34.     if [[ ${trafficValue%.*} -gt 922 ]];then
  35.         #conv to Kb
  36.         trafficValue=`awk -v value=$trafficValue ‘BEGIN{printf "%0.1f",value/1024}’`
  37.         if [[ ${trafficValue%.*} -gt 922 ]];then
  38.             #conv to Mb
  39.             trafficValue=`awk -v value=$trafficValue ‘BEGIN{printf "%0.1f",value/1024}’`
  40.             echo "${trafficValue}Mb"
  41.         else
  42.             echo "${trafficValue}Kb"
  43.         fi
  44.     else
  45.         echo "${trafficValue}b"
  46.     fi
  47. }
  48.  
  49. #判断包管理工具
  50. check_package_manager(){
  51.     local manager=$1
  52.     local systemPackage=”
  53.     if cat /etc/issue | grep -q -E -i "ubuntu|debian";then
  54.         systemPackage=’apt’
  55.     elif cat /etc/issue | grep -q -E -i "centos|red hat|redhat";then
  56.         systemPackage=’yum’
  57.     elif cat /proc/version | grep -q -E -i "ubuntu|debian";then
  58.         systemPackage=’apt’
  59.     elif cat /proc/version | grep -q -E -i "centos|red hat|redhat";then
  60.         systemPackage=’yum’
  61.     else
  62.         echo "unkonw"
  63.     fi
  64.  
  65.     if [ "$manager" == "$systemPackage" ];then
  66.         return 0
  67.     else
  68.         return 1
  69.     fi   
  70. }
  71.  
  72.  
  73. #实时流量
  74. realTimeTraffic(){
  75.     local eth=""
  76.     local nic_arr=(`ifconfig | grep -E -o "^[a-z0-9]+" | grep -v "lo" | uniq`)
  77.     local nicLen=${#nic_arr[@]}
  78.     if [[ $nicLen -eq 0 ]]; then
  79.         echo "sorry,I can not detect any network device,please report this issue to author."
  80.         exit 1
  81.     elif [[ $nicLen -eq 1 ]]; then
  82.         eth=$nic_arr
  83.     else
  84.         display_menu nic
  85.         eth=$nic
  86.     fi   
  87.  
  88.     local clear=true
  89.     local eth_in_peak=0
  90.     local eth_out_peak=0
  91.     local eth_in=0
  92.     local eth_out=0
  93.  
  94.     while true;do
  95.         #移动光标到0:0位置
  96.         printf "33[0;0H"
  97.         #清屏并打印Now Peak
  98.         [[ $clear == true ]] && printf "33[2J" && echo "$eth——–Now——–Peak———–"
  99.         traffic_be=(`awk -v eth=$eth -F'[: ]+’ ‘{if ($0 ~eth){print $3,$11}}’ /proc/net/dev`)
  100.         sleep 2
  101.         traffic_af=(`awk -v eth=$eth -F'[: ]+’ ‘{if ($0 ~eth){print $3,$11}}’ /proc/net/dev`)
  102.         #计算速率
  103.         eth_in=$(( (${traffic_af[0]}-${traffic_be[0]})*8/2 ))
  104.         eth_out=$(( (${traffic_af[1]}-${traffic_be[1]})*8/2 ))
  105.         #计算流量峰值
  106.         [[ $eth_in -gt $eth_in_peak ]] && eth_in_peak=$eth_in
  107.         [[ $eth_out -gt $eth_out_peak ]] && eth_out_peak=$eth_out
  108.         #移动光标到2:1
  109.         printf "33[2;1H"
  110.         #清除当前行
  111.         printf "33[K"   
  112.         printf "%-20s %-20sn" "Receive:  $(bit_to_human_readable $eth_in)" "$(bit_to_human_readable $eth_in_peak)"
  113.         #清除当前行
  114.         printf "33[K"
  115.         printf "%-20s %-20sn" "Transmit: $(bit_to_human_readable $eth_out)" "$(bit_to_human_readable $eth_out_peak)"
  116.         [[ $clear == true ]] && clear=false
  117.     done
  118. }
  119.  
  120. #流量和连接概览
  121. trafficAndConnectionOverview(){
  122.     if ! which tcpdump > /dev/null;then
  123.         echo "tcpdump not found,going to install it."
  124.         if check_package_manager apt;then
  125.             apt-get -y install tcpdump
  126.         elif check_package_manager yum;then
  127.             yum -y install tcpdump
  128.         fi
  129.     fi
  130.  
  131.     local reg=""
  132.     local eth=""
  133.     local nic_arr=(`ifconfig | grep -E -o "^[a-z0-9]+" | grep -v "lo" | uniq`)
  134.     local nicLen=${#nic_arr[@]}
  135.     if [[ $nicLen -eq 0 ]]; then
  136.         echo "sorry,I can not detect any network device,please report this issue to author."
  137.         exit 1
  138.     elif [[ $nicLen -eq 1 ]]; then
  139.         eth=$nic_arr
  140.     else
  141.         display_menu nic
  142.         eth=$nic
  143.     fi
  144.  
  145.     echo "please wait for 10s to generate network data…"
  146.     echo
  147.     #当前流量值
  148.     local traffic_be=(`awk -v eth=$eth -F'[: ]+’ ‘{if ($0 ~eth){print $3,$11}}’ /proc/net/dev`)
  149.     #tcpdump监听网络
  150.     tcpdump -v -i $eth -tnn > /tmp/tcpdump_temp 2>&1 &
  151.     sleep 10
  152.     clear
  153.     kill `ps aux | grep tcpdump | grep -v grep | awk ‘{print $2}’`
  154.  
  155.     #10s后流量值
  156.     local traffic_af=(`awk -v eth=$eth -F'[: ]+’ ‘{if ($0 ~eth){print $3,$11}}’ /proc/net/dev`)
  157.     #打印10s平均速率
  158.     local eth_in=$(( (${traffic_af[0]}-${traffic_be[0]})*8/10 ))
  159.     local eth_out=$(( (${traffic_af[1]}-${traffic_be[1]})*8/10 ))
  160.     echo -e "33[32mnetwork device $eth average traffic in 10s: 33[0m"
  161.     echo "$eth Receive: $(bit_to_human_readable $eth_in)/s"
  162.     echo "$eth Transmit: $(bit_to_human_readable $eth_out)/s"
  163.     echo
  164.  
  165.     local regTcpdump=$(ifconfig | grep -A 1 $eth | awk -F'[: ]+’ ‘$0~/inet addr:/{printf $4"|"}’ | sed -e ‘s/|$//’ -e ‘s/^/(/’ -e ‘s/$/)\\.[0-9]+:/’)
  166.  
  167.     #新旧版本tcpdump输出格式不一样,分别处理
  168.     if awk ‘/^IP/{print;exit}’ /tmp/tcpdump_temp | grep -q ")$";then
  169.         #处理tcpdump文件
  170.         awk ‘/^IP/{print;getline;print}’ /tmp/tcpdump_temp > /tmp/tcpdump_temp2
  171.     else
  172.         #处理tcpdump文件
  173.         awk ‘/^IP/{print}’ /tmp/tcpdump_temp > /tmp/tcpdump_temp2
  174.         sed -i -r ‘s#(.*: [0-9]+))(.*)#1n    2#’ /tmp/tcpdump_temp2
  175.     fi
  176.     
  177.     awk ‘{len=$NF;sub(/)/,"",len);getline;print $0,len}’ /tmp/tcpdump_temp2 > /tmp/tcpdump
  178.  
  179.     #统计每个端口在10s内的平均流量
  180.     echo -e "33[32maverage traffic in 10s base on server port: 33[0m"
  181.     awk -F'[ .:]+’ -v regTcpdump=$regTcpdump ‘{if ($0 ~ regTcpdump){line="clients > "$8"."$9"."$10"."$11":"$12}else{line=$2"."$3"."$4"."$5":"$6" > clients"};sum[line]+=$NF*8/10}END{for (line in sum){printf "%s %dn",line,sum[line]}}’ /tmp/tcpdump |
  182.     sort -k 4 -nr | head -n 10 | while read a b c d;do
  183.         echo "$a $b $c $(bit_to_human_readable $d)/s"
  184.     done
  185.     echo -ne "33[11A"
  186.     echo -ne "33[50C"
  187.     echo -e "33[32maverage traffic in 10s base on client port: 33[0m"
  188.     awk -F'[ .:]+’ -v regTcpdump=$regTcpdump ‘{if ($0 ~ regTcpdump){line=$2"."$3"."$4"."$5":"$6" > server"}else{line="server > "$8"."$9"."$10"."$11":"$12};sum[line]+=$NF*8/10}END{for (line in sum){printf "%s %dn",line,sum[line]}}’ /tmp/tcpdump |
  189.     sort -k 4 -nr | head -n 10 | while read a b c d;do
  190.             echo -ne "33[50C"
  191.             echo "$a $b $c $(bit_to_human_readable $d)/s"
  192.     done   
  193.         
  194.     echo
  195.  
  196.     #统计在10s内占用带宽最大的前10个ip
  197.     echo -e "33[32mtop 10 ip average traffic in 10s base on server: 33[0m"
  198.     awk -F'[ .:]+’ -v regTcpdump=$regTcpdump ‘{if ($0 ~ regTcpdump){line=$2"."$3"."$4"."$5" > "$8"."$9"."$10"."$11":"$12}else{line=$2"."$3"."$4"."$5":"$6" > "$8"."$9"."$10"."$11};sum[line]+=$NF*8/10}END{for (line in sum){printf "%s %dn",line,sum[line]}}’ /tmp/tcpdump |
  199.     sort -k 4 -nr | head -n 10 | while read a b c d;do
  200.         echo "$a $b $c $(bit_to_human_readable $d)/s"
  201.     done
  202.     echo -ne "33[11A"
  203.     echo -ne "33[50C"
  204.     echo -e "33[32mtop 10 ip average traffic in 10s base on client: 33[0m"
  205.     awk -F'[ .:]+’ -v regTcpdump=$regTcpdump ‘{if ($0 ~ regTcpdump){line=$2"."$3"."$4"."$5":"$6" > "$8"."$9"."$10"."$11}else{line=$2"."$3"."$4"."$5" > "$8"."$9"."$10"."$11":"$12};sum[line]+=$NF*8/10}END{for (line in sum){printf "%s %dn",line,sum[line]}}’ /tmp/tcpdump |
  206.     sort -k 4 -nr | head -n 10 | while read a b c d;do
  207.         echo -ne "33[50C"
  208.         echo "$a $b $c $(bit_to_human_readable $d)/s"
  209.     done
  210.  
  211.     echo
  212.     #统计连接状态
  213.     local regSS=$(ifconfig | grep -A 1 $eth | awk -F'[: ]+’ ‘$0~/inet addr:/{printf $4"|"}’ | sed -e ‘s/|$//’)
  214.     ss -an | grep -v -E "LISTEN|UNCONN" | grep -E "$regSS" > /tmp/ss
  215.     echo -e "33[32mconnection state count: 33[0m"
  216.     awk ‘NR>1{sum[$(NF-4)]+=1}END{for (state in sum){print state,sum[state]}}’ /tmp/ss | sort -k 2 -nr
  217.     echo
  218.     #统计各端口连接状态
  219.     echo -e "33[32mconnection state count by port base on server: 33[0m"
  220.     awk ‘NR>1{sum[$(NF-4),$(NF-1)]+=1}END{for (key in sum){split(key,subkey,SUBSEP);print subkey[1],subkey[2],sum[subkey[1],subkey[2]]}}’ /tmp/ss | sort -k 3 -nr | head -n 10   
  221.     echo -ne "33[11A"
  222.     echo -ne "33[50C"
  223.     echo -e "33[32mconnection state count by port base on client: 33[0m"
  224.     awk ‘NR>1{sum[$(NF-4),$(NF)]+=1}END{for (key in sum){split(key,subkey,SUBSEP);print subkey[1],subkey[2],sum[subkey[1],subkey[2]]}}’ /tmp/ss | sort -k 3 -nr | head -n 10 | awk ‘{print "33[50C"$0}’   
  225.     echo   
  226.     #统计端口为80且状态为ESTAB连接数最多的前10个IP
  227.     echo -e "33[32mtop 10 ip ESTAB state count at port 80: 33[0m"
  228.     cat /tmp/ss | grep ESTAB | awk -F'[: ]+’ ‘{sum[$(NF-2)]+=1}END{for (ip in sum){print ip,sum[ip]}}’ | sort -k 2 -nr | head -n 10
  229.     echo
  230.     #统计端口为80且状态为SYN-RECV连接数最多的前10个IP
  231.     echo -e "33[32mtop 10 ip SYN-RECV state count at port 80: 33[0m"
  232.     cat /tmp/ss | grep -E "$regSS" | grep SYN-RECV | awk -F'[: ]+’ ‘{sum[$(NF-2)]+=1}END{for (ip in sum){print ip,sum[ip]}}’ | sort -k 2 -nr | head -n 10
  233. }
  234.  
  235. main(){
  236.     while true; do
  237.         echo -e "1) real time traffic.n2) traffic and connection overview.n"
  238.         read -p "please input your select(ie 1): " select
  239.         case  $select in
  240.             1) realTimeTraffic;break;;
  241.             2) trafficAndConnectionOverview;break;;
  242.             *) echo "input error,please input a number.";;
  243.         esac
  244.     done   
  245. }
  246.  
  247. main

脚本中如有不明白的地方,可以留言咨询。

CDNFly(原HttpGuard)介绍

CDNFly是基于openresty,以lua脚本语言开发的防cc攻击软件。而openresty是集成了高性能web服务器Nginx,以及一系列的Nginx模块,这其中最重要的,也是我们主要用到的nginx lua模块。CDNFly基于nginx lua开发,继承了nginx高并发,高性能的特点,可以以非常小的性能损耗来防范大规模的cc攻击。

功能介绍

多节点管理

CDNFly支持多节点管理,可以在主控端同步更新同一个节点组的防cc配置,nginx配置等

网站管理

支持在控制面板添加,编辑,删除网站

防CC功能

内置多种防CC方法,以应付不同的攻击强度,如根据请求频率,浏览器自动识别,人工滑动验证,人工验证码等。
除了内置的这几种规则,还可以自定义自己的规则防护。

自动开启防护

在未受攻击的时候我们不想开启防攻击模式以免影响用户体验,这时候可以使用自动开启防护的功能。可以设置好开启防护的条件来自动开启防护。

允许爬虫

在开启防护的时候,像百度,谷歌,搜狗等为我们带来流量的搜索引擎的爬虫可能就无法正常访问我们的网站,这时候我们开启允许爬虫的功能,就可以免受防攻击模式的影响。

监控中心

添加新节点后,自动配置对域名请求频率、域名带宽的监控,以及节点Nginx性能、负载、硬盘、网络等监控。方便我们来查看节点及域名的运行情况,或者根据监控数据来查找问题。

界面截图

节点管理

服务器安全

网站管理

服务器安全

防护规则

服务器安全

域名监控

服务器安全

节点监控

服务器安全

安装和演示

安装:http://devops.webres.wang/2017/06/httpguard-installation/
演示:超级管理员:http://httpguard-demo.webres.wang:88/admin/login.html(admin/guard)
普通用户:http://httpguard-demo.webres.wang:88/user/login.html(user/user)

更多CDNFly文档

http://devops.webres.wang/tag/httpguard/

联系作者

QQ: 452336092

Nginx反向代理谷歌

这几天谷歌都被某墙折腾得不能用了啊。上谷歌搜索资料都要翻越某墙,真悲摧啊。干脆自己用国外的一个vps反向代理谷歌吧。下面贴出我反向代理谷歌的nginx配置,造福人类。
假设用devops.webres.wang反向代理谷歌,是假设,我可不愿意由于这个博客被墙。

  1. proxy_cache_path  /data/nginx/cache/one  levels=1:2   keys_zone=one:10m max_size=10g;
  2. proxy_cache_key  "$host$request_uri";
  3. server {
  4. listen 80;
  5. server_name devops.webres.wang webres.wang;
  6. rewrite ^(.*) http://devops.webres.wang$1 permanent;
  7. }
  8.  
  9. upstream google {
  10.  server 74.125.224.80:80 max_fails=3;
  11.  server 74.125.224.81:80 max_fails=3;
  12.  server 74.125.224.82:80 max_fails=3;
  13.  server 74.125.224.83:80 max_fails=3;
  14.  server 74.125.224.84:80 max_fails=3;   
  15.  }
  16. server {
  17.         listen      443;
  18.         server_name  devops.webres.wang webres.wang;
  19.          ssl on;
  20.     ssl_certificate /usr/local/nginx/conf/centos.crt;
  21.     ssl_certificate_key /usr/local/nginx/conf/centos.key;
  22. location / {
  23.                 proxy_cache one;
  24.                 proxy_cache_valid  200 302  1h;
  25.                 proxy_cache_valid  404      1m;
  26.                 proxy_redirect https://www.google.com/ /;
  27.                 proxy_cookie_domain google.com webres.wang;
  28.                 proxy_pass              http://google;
  29.                 proxy_set_header Host "www.google.com";
  30.                 proxy_set_header Accept-Encoding "";
  31.                 proxy_set_header User-Agent $http_user_agent;
  32.                 proxy_set_header Accept-Language "zh-CN";
  33.                 proxy_set_header Cookie "PREF=ID=047808f19f6de346:U=0f62f33dd8549d11:FF=2:LD=zh-CN:NW=1:TM=1325338577:LM=1332142444:GM=1:SG=2:S=rE0SyJh2w1IQ-Maw";             
  34.                 sub_filter www.google.com devops.webres.wang;
  35.                 sub_filter_once off;
  36. }
  37. }

解释下吧:

  • 1、这里监听了80和443端口,用了ssl加密,高大上。ssl证书是免费的,startssl,自己去申请个吧。
  • 2、定义了个upstream google,放了5个谷歌的ip,如果不这样做,就等着被谷歌的验证码搞崩溃吧。
  • 3、也设置了反向代理缓存,某些资源不用重复去请求谷歌获取,加快搜索速度。
  • 4、proxy_redirect https://www.google.com/ /; 这行的作用是把谷歌服务器返回的302响应头里的域名替换成我们的,不然浏览器还是会直接请求www.google.com,那样反向代理就失效了。
  • 5、proxy_cookie_domain google.com webres.wang; 把cookie的作用域替换成我们的域名。
  • 6、proxy_pass http://google; 反向代理到upstream google,会随机把请求分配到那几个ip。忘记说了,那几个ip可以在自己的vps或服务器上使用nslookup www.google.com获取。
  • 7、proxy_set_header Accept-Encoding “”; 防止谷歌返回压缩的内容,因为压缩的内容我们无法作域名替换。
  • 8、proxy_set_header Accept-Language “zh-CN”;设置语言为中文
  • 9、proxy_set_header Cookie “PREF=ID=047808f19f6de346:U=0f62f33dd8549d11:FF=2:LD=zh-CN:NW=1:TM=1325338577:LM=1332142444:GM=1:SG=2:S=rE0SyJh2w1IQ-Maw”; 这行很关键,传固定的cookie给谷歌,是为了禁止即时搜索,因为开启即时搜索无法替换内容。还有设置为新窗口打开网站,这个符合我们打开链接的习惯。
  • 10、sub_filter www.google.com devops.webres.wang;当然是把谷歌的域名替换成我们的了,注意需要安装nginx的sub_filter模块

好了,说明够详细了,祝各位享受到高质量的谷歌搜索。

当安全协议不安全了:OpenSSL漏洞

此漏洞危害特别严重,请重视。

 

今天是一个特别的日子,早上大家还在讨论XP停止服务的事,到处是相关的新闻和文章,到了下午,到处都是OpenSSL的漏洞消息了。

OpenSSL与SSL安全协议

什么是SSL安全协议,我记得在10年我写过一篇简单介绍的文章,小谈SSL安全协议(小谈SSL安全协议(原创)_Fooying_百度空间),大家不凡可以看看,以前的文章,大家就不要笑话了。

SSL,全称Secure Socket Layer,为Netscape所研发,用以保障在Internet上数据传输之安全,利用数据加密(Encryption)技术,可确保数据在网络上之传输过程中不会被截取及窃听。简单的说,就是加密传输的数据,避免被截取监听等。

SSL应该是大家平时接触最多的安全协议了,大家可以看访问一些网址的时候,一般是http://开头,如果发现https://开头就是采用了SSL安全协议。比如,大家在登陆微信网页版的时候就可以看到:

服务器安全

一般来说,比如nginx,可以通过以下方式就可以进行配置:

# HTTPS server 
# 
server { 
listen 443; 
server_name localhost; 
 
ssl on; 
ssl_certificate /opt/nginx/sslkey/server.crt; 
ssl_certificate_key /opt/nginx/sslkey/server.key; 
 
location / {     
		root /home/workspace/;     
		index index.asp index.aspx;        
	} 
} 

 

大家可以看到,监听的是443端口,然后通过ssl on;来开启,同时通过ssl_certificate和sl_certificate_key配置证书和key文件,具体的就不多解释了,大家可以自己搜索下。

那么证书和key又是怎么一回事呢?接下来就要讲到OpenSSL了。

进行过nginx编译的同学都知道,在编译nginx的时候,如果想让nginx支持开启SSL,那么必须加一个–with-http_ssl_module 的配置项。那么又何让服务器支持这个配置项呢?又如何生成nginx配置中SSL所需要的证书和key文件呢?都是源于OpenSSL(openssl_百度百科)。

OpenSSL是一个强大的安全套接字层密码库,Apache使用它加密HTTPS,OpenSSH使用它加密SSH,但是,你不应该只将其作为一个库来使用,它还是一个多用途的、跨平台的密码工具。

大家平时如果采用公私钥的方式连接服务器,也是需要用到OpenSSL的。简单的理解,OpenSSL是一个强大的支持库,更是一个强大的密码工具。虽然要支持SSL协议不一定得采用OpenSSL,但是基本大部分的都是采用OpenSSL。

心脏出血的OpenSSL

相信前面简单的介绍能让大家了解到OpenSSL的重要性,也明白了SSL协议是做什么的,那么大家应该就可以理解,本来采用SSL协议是为了数据传输的安全性,是为了更安全,但是OpenSSL的漏洞直接导致了本该是让为了更安全的设置变成了致命的危险。

简单介绍下漏洞,这个漏洞是昨天国外的黑客曝光的,该漏洞可以获取HTTPS服务器的随机64K内存。这个漏洞被称为heartbleed,直译的话就是心脏出血。可能有部分同学没意识到这个64K有啥用错,读取内存又有啥用?

贴几张图:

这是笔者利用poc进行的一些测试(测试poc:http://s3.jspenguin.org/ssltest.py,直接python ssltest.py domain就可以了),大家可以看到,cookie甚至是明文帐号密码都直接爆出来了,有的还有代码源码(这个忘记截图了。。。)、SSL私钥(这个笔者没测试出来)等,那么影响有多大呢?看下wooyun的漏洞提交列表:

再给个来自zoomeye(ZoomEye – The Cyberspace Search Engine)的统计数据:

全国443端口:1601250,有33303个受本次OpenSSL漏洞影响

看了这些,称为”心脏出血”完全不为过。今天估计又有许多运维同学可忙的了。具体的漏洞分析我也不多说了,大家可以看wooyun上的文章(关于OpenSSL“心脏出血”漏洞的分析

安全防范

说了这么多,可能有些同学觉得都不要去访问那些是https的网站了,其实也大可不必,官方其实已经放出补丁了,修复方法:

升级到最新版本OpenSSL 1.0.1g
无法立即升级的用户可以以-DOPENSSL_NO_HEARTBEATS开关重新编译OpenSSL
1.0.2-beta版本的漏洞将在beta2版本修复

对于个人用户的话,大家不用太担心,虽然说影响有点大,不过问题主要是出在服务商,而且像那些大网站,比如微信、淘宝等都已经修复了,实在不放心,大家可以这几天暂时不要访问使用了SSL协议的网站进行登陆等操作,特别是网银等网站。另外,提供个在线检测的工具给大家:Test your server for Heartbleed (CVE-2014-0160)与我们团队弄的一个工具实验室 – SCANV,大家也可以访问前进行下检测。

结语

剩下的就不多说了,首先给那些连夜处理的运维和安全工作人员道声辛苦了。现在大家对安全越来越重视,这是好事,剩下的就是大家上网多小心些!

转自:http://zhuanlan.zhihu.com/fooying/19722474

ubuntu apt-file解决依赖的利器

刚才安装Spynner模块,使用的是easy_install spynner安装,各种文件缺失啊,无意中发现apt-file命令,从此再也不用担心编译软件缺少某个文件而不知所措了。
1、首先得安装apt-file命令

  1. apt-get install apt-file

2、接着更新apt-file的缓存

  1. apt-file update

3、现在你可以使用apt-file搜索缺失的文件了,比如编译过程中提示缺少

  1. root@test:~# apt-file search XTest.h
  2. libbcprov-java-doc: /usr/share/doc/libbcprov-java-doc/api/org/bouncycastle/jce/provider/test/PKIXTest.html
  3. libreoffice-dev-doc: /usr/share/doc/libreoffice-dev/docs/common/ref/com/sun/star/test/XTest.html
  4. libsvgsalamander-java-doc: /usr/share/doc/libsvgsalamander-java-doc/api/com/kitfox/svg/xml/cpx/CPXTest.html
  5. libsvgsalamander-java-doc: /usr/share/doc/libsvgsalamander-java-doc/api/com/kitfox/svg/xml/cpx/class-use/CPXTest.html
  6. libxtst-dev: /usr/include/X11/extensions/XTest.h

4、显然libxtst-dev包正是我们需要安装的

  1. apt-get install libxtst-dev