MySQL修改账号授权的的IP地址

今天遇到一个需求:修改MySQL用户的权限,需要限制特定IP地址才能访问,第一次遇到这类需求,结果在测试过程,使用更新系统权限报发现出现了一些问题, 具体演示如下. 下面测试环境为MySQL 5.6.20. 如有其它版本与下面测试结果有出入,请以实际环境为准。

我们先创建一个测试用户LimitIP,只允许192.168段的IP地址访问,具体权限如下所示:

mysql> GRANT SELECT ON MyDB.* TO LimitIP@'192.168.%' IDENTIFIED BY 'LimitIP';
Query OK, 0 rows affected (0.01 sec)

mysql> GRANT INSERT ,UPDATE,DELETE ON MyDB.kkk TO LimitIP@'192.168.%';
Query OK, 0 rows affected (0.00 sec)

mysql> 
mysql> flush privileges;
Query OK, 0 rows affected (0.00 sec)

mysql> 

mysql> show grants for LimitIP@'192.168.%';
+----------------------------------------------------------------------------------------------------------------+
| Grants for [email protected].%                                                                                   |
+----------------------------------------------------------------------------------------------------------------+
| GRANT USAGE ON *.* TO 'LimitIP'@'192.168.%' IDENTIFIED BY PASSWORD '*72DDE03E02CC55A9478A82F3F4EBE7F639249DEC' |
| GRANT SELECT ON `MyDB`.* TO 'LimitIP'@'192.168.%'                                                              |
| GRANT INSERT, UPDATE, DELETE ON `MyDB`.`kkk` TO 'LimitIP'@'192.168.%'                                          |
+----------------------------------------------------------------------------------------------------------------+
3 rows in set (0.00 sec)

mysql>

未分类

假设现在收到需求:这个用户只允许这个IP地址192.168.103.17访问,于是我打算更新mysql.user表,如下所示:

mysql> select user, host from mysql.user where user='LimitIP';
+---------+-----------+
| user    | host      |
+---------+-----------+
| LimitIP | 192.168.% |
+---------+-----------+
1 row in set (0.00 sec)

mysql> update mysql.user set host='192.168.103.17' where user='LimitIP';
Query OK, 1 row affected (0.02 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> flush privileges;
Query OK, 0 rows affected (0.01 sec)

mysql> select user, host from user where user='LimitIP';
ERROR 1046 (3D000): No database selected
mysql> use mysql;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
mysql> select user, host from user where user='LimitIP';
+---------+----------------+
| user    | host           |
+---------+----------------+
| LimitIP | 192.168.103.17 |
+---------+----------------+
1 row in set (0.00 sec)

mysql> show grants for LimitIP@'192.168.103.17';
+---------------------------------------------------------------------------------------------------------------------+
| Grants for [email protected]                                                                                   |
+---------------------------------------------------------------------------------------------------------------------+
| GRANT USAGE ON *.* TO 'LimitIP'@'192.168.103.17' IDENTIFIED BY PASSWORD '*72DDE03E02CC55A9478A82F3F4EBE7F639249DEC' |
+---------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

mysql> 

未分类

上面测试发现,如果这样只修改mysql.user表, 那么之前的权限没有了,如下所示,如果你查询mysql.db、 mysql.tables_priv 发现Host的字段值依然为192.168.%

mysql>  select * from mysql.db where user='LimitIP'G;
*************************** 1. row ***************************
                 Host: 192.168.%
                   Db: MyDB
                 User: LimitIP
          Select_priv: Y
          Insert_priv: N
          Update_priv: N
          Delete_priv: N
          Create_priv: N
            Drop_priv: N
           Grant_priv: N
      References_priv: N
           Index_priv: N
           Alter_priv: N
Create_tmp_table_priv: N
     Lock_tables_priv: N
     Create_view_priv: N
       Show_view_priv: N
  Create_routine_priv: N
   Alter_routine_priv: N
         Execute_priv: N
           Event_priv: N
         Trigger_priv: N
1 row in set (0.00 sec)

ERROR: 
No query specified

mysql> select * from mysql.tables_priv where user='LimitIP'G;
*************************** 1. row ***************************
       Host: 192.168.%
         Db: MyDB
       User: LimitIP
 Table_name: kkk
    Grantor: root@localhost
  Timestamp: 0000-00-00 00:00:00
 Table_priv: Insert,Update,Delete
Column_priv: 
1 row in set (0.00 sec)

ERROR: 
No query specified

所以我继续修改 mysql.db、 mysql.tables_priv 表,然后测试验证终于OK了(请见下面测试步骤),当然如果账户的权限不止这几个层面,你可能还必须修改例如mysql.columns_priv、mysql.procs_priv等表

mysql> show grants for LimitIP@'192.168.%';
ERROR 1141 (42000): There is no such grant defined for user 'LimitIP' on host '192.168.%'
mysql> 
mysql> 
mysql> update mysql.db set host='192.168.103.17' where user='LimitIP';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> update mysql.tables_priv set host='192.168.103.17' where user='LimitIP';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> flush privileges;
Query OK, 0 rows affected (0.00 sec)

mysql> show grants for LimitIP@'192.168.103.17';
+---------------------------------------------------------------------------------------------------------------------+
| Grants for [email protected]                                                                                   |
+---------------------------------------------------------------------------------------------------------------------+
| GRANT USAGE ON *.* TO 'LimitIP'@'192.168.103.17' IDENTIFIED BY PASSWORD '*72DDE03E02CC55A9478A82F3F4EBE7F639249DEC' |
| GRANT SELECT ON `MyDB`.* TO 'LimitIP'@'192.168.103.17'                                                              |
| GRANT INSERT, UPDATE, DELETE ON `MyDB`.`kkk` TO 'LimitIP'@'192.168.103.17'                                          |
+---------------------------------------------------------------------------------------------------------------------+
3 rows in set (0.00 sec)

mysql> 

未分类

如果需要修改用户的IP限制,其实更新mysql相关权限表不是上上策,其实有更好的方法,那就是RENAME USER Syntax (https://dev.mysql.com/doc/refman/5.6/en/rename-user.html)

mysql> RENAME USER 'LimitIP'@'192.168.103.17' TO 'LimitIP'@'192.168.103.18';
Query OK, 0 rows affected (0.00 sec)

mysql> FLUSH PRIVILEGES;
Query OK, 0 rows affected (0.00 sec)

mysql> show grants for 'LimitIP'@'192.168.103.18';
+---------------------------------------------------------------------------------------------------------------------+
| Grants for [email protected]                                                                                   |
+---------------------------------------------------------------------------------------------------------------------+
| GRANT USAGE ON *.* TO 'LimitIP'@'192.168.103.18' IDENTIFIED BY PASSWORD '*72DDE03E02CC55A9478A82F3F4EBE7F639249DEC' |
| GRANT SELECT ON `MyDB`.* TO 'LimitIP'@'192.168.103.18'                                                              |
| GRANT INSERT, UPDATE, DELETE ON `MyDB`.`kkk` TO 'LimitIP'@'192.168.103.18'                                          |
+---------------------------------------------------------------------------------------------------------------------+
3 rows in set (0.00 sec)

mysql> 

Ubuntu 16.04 LTS 安装Mongodb 3.4

第一步:安装

未分类

未分类

未分类

#setp 1. Import the public key used by the package management system.
sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 0C49F3730359A14518585931BC711F9BA15703C6

未分类

#step 2. Create a list file for MongoDB
echo "deb [ arch=amd64,arm64 ] http://repo.mongodb.org/apt/ubuntu xenial/mongodb-org/3.4 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-3.4.list

未分类

#step 3. Reload local package database
sudo apt-get update

未分类

#step 4. Install the latest stable version of MongoDB
sudo apt-get install -y mongodb-org

未分类

第二步:启动服务

启动mongodb服务,默认安装后,是启动mongodb服务的

sudo service mongod stop  #停止服务
sudo service mongod start  #启动服务
sudo service mongod restart #重新启动服务
sudo service mongod status #查看状态

未分类

允许开机启动,默认当重启服务器后,mongodb服务会停止,需要设置开机启动mongodb服务

sudo systemctl enable mongod

未分类

第三步:连接

本机连接至mongodb服务,使用mongo命令连接

mongo

未分类

mongodb配置文件

less /etc/mongod.conf

未分类

客户端连接,mongo安装完成后,默认是只能在本机连接,在服务器外部是不能连接mongo的

#切换至root用户
sudo -i
#修改mongo配置文件
vim /etc/mongod.conf

未分类

修改完成后,保存文件,并重启mongo

#退出root用户
exit
#重启服务
sudo service mongod restart

客户端连接成功

未分类

第四步:删除mongodb

请参考文档《Install MongoDB Community Edition on Ubuntu》 (https://docs.mongodb.com/manual/tutorial/install-mongodb-on-ubuntu/#uninstall-mongodb-community-edition)

#停止mongodb服务
sudo service mongod stop
#删除包
sudo apt-get purge mongodb-org*
#删除数据文件及日志文件
sudo rm -r /var/log/mongodb
sudo rm -r /var/lib/mongodb

mongodb日志存储优化

背景

之前写的代码所有的日志都通过mongodb来存储和检索,目前硬盘200G已经使用了195G,监控一直报警。一开始通过精简已有日志中的字段,发现空间下降不明显。于是翻了下官方手册,刚好翻到mongodb的备份,想了想历史日志也用不着及时搜索,于是就备份了

备份命令

mongodump --archive=xxxx.archive --db db --collection collection

压缩备份

gzip xxxx.archive

写个脚本删除备份的日志

后续

备份恢复命令

mongorestore --gzip --archive=xxxx.archive.gz --db db

shell脚本

由于我的日志是按照日期分天存储,因此比较好导出,脚本如下

#!/bin/bash
a="mongodump --archive="
b=".archive"
c="--db php_log --collection "
for i in $( seq $1 $2 )
do
    collection="$3_$i"
    archive="$collection$b"
    $a$archive $c$collection
    gzip $archive
done

脚本比较烂,不要嘲笑????

删除1/2日志成果

删除前:剩余5G 删除后:剩余95G 数据压缩包:5G 节约空间90G,不得不说mongo很吃硬盘啊(更吃内存)

Mongodb开启身份验证

1. 介绍

不管数据库是在多安全的环境或者本地环境,给数据库建立一个安全的环境是很有必要的。

Mongodb提供了一系列的安全功能,这里介绍一种很常用的身份验证方式。

2. 开启验证

默认情况下,只要在启动数据库的时候没有加上–auth选项,就是没有身份验证功能的,所有客户端都可以进行所有权限的操作。

如果加上过后,我们就可以通过安全的身份验证连接数据库。如果要在数据库中进行身份验证,可以通过db.auth(username, password),如果验证成功则返回1,反之。

3. 建立用户

建立用户我们可以通过db.createUser()方法来建立用户,比如下面这样:

db.createUser({user: 'username', pwd: 'password', roles: [
    {role: 'read', db: 'test'}
]});

db.createUser方法的接受一个对象,里面的user代表用户名,pwd代表密码,而roles是一个数组可以接受多个对象,每个对象可以对应作用于的数据库,其中的role字段代表对作用的数据库的权限,官方规定了一些列的内置角色,可以通过文档查询。

4. 删除用户

删除用户需要具有权限的用户进行操作,通过db.dropUser()方法进行,接受一个字符串,这个字符串就是用户名:

db.dropUser('user1');

5. 获取用户

可以通过db.getUser()方法来获取用户信息,同样它接受一个字符串,字符串为用户名:

db.getUser('user1');

启用memcached动态缓存加速wordpress

概述

扉启博客正在使用的是基于nginx的fastcgi纯静态缓存,这是将所有的动态HTML页面都缓存到硬盘文件,nginx针对http请求只处理静态内容,因此对服务器的开销很小,速度快。对于动态内容不多的站点,用这个方法能极大缓解cpu的负担,由nginx来高效地处理并发。

另一种缓存方式是基于memcached缓存动态内容,将数据库的数据缓存在内存中,下次需要的时候直接从内存中取数据,减少MySQL的访问次数,也加速了wordpress对网页的处理。这种方式直接从内存中存取数据,理论上比静态缓存的IO开销更小,但是由于memcached需要占用一定php资源,因此会对CPU带来一些额外的压力。

本文记述了在另一个站点上安装部署memcached的过程,最后测试这种方式的缓存的响应速度和并发处理能力。

未分类

安装memcached服务

网上有一些文章提供了memcached服务的安装方法,有的手动下载源码编译安装,这里采用军哥lnmp一键包插件安装简单的方法。在lnmp源代码目录下,运行addon.sh脚本安装memcached服务

./addons.sh install memcached

在随后出现的选项里选择2,也就是php-memcached的带d的版本,这个相对不带d的版本更新,性能更好。

编译安装完成以后,检查一下memcached的服务是否已经运行。

systemctl status memcached
memcached.service - LSB: memcached - Memory caching daemon
   Loaded: loaded (/etc/rc.d/init.d/memcached; bad; vendor preset: disabled)
   Active: active (running) since Fri 2017-08-18 14:38:00 CST; 1 weeks 0 days ago

然后检查php的memcached模块是否已经加载

php -m | grep memcached

可以新建一个php文件测一下缓存功能,将下面的代码保存为test.php文件。

<?php
$m = new Memcached();
$m->addServer( '127.0.0.1', 11211 );
$m->set( 'foo', 100 );
echo $m->get( 'foo' ) . "n";

运行php -f test.php,如果结果是100的话表明memcached正常运行。
都没问题的话,就可以进行下一步安装wordpress插件

安装wordpress的memcached插件

访问github项目下载插件文件object-cache.php

将下载好的文件放入wordpress网站目录的wp-content/下,此时wordpress已自动利用memcached缓存功能。

缓存HTML页面到内存

到前面这一步已经完成了数据库查询的动态缓存,如果想要进一步提高性能,还可以类似与wp supercache或fastcgi缓存一下将网站的页面静态化,只不过存储在memcached分配的内存中,而不是硬盘上的文件。

这里要用到的插件叫做batcache,代码的readme文件里解释了这个名称的由来。bat并不表示真的和蝙蝠有什么联系,而是正好发布前夕wp-supercache已经发布,为了不和其他缓存插件冲突而用了这个名字。

首先下载官方插件项目的代码包batcache,解压缩后得到了advanced-cache.php文件,将其放入wordpress网站目录的wp-content/下。

然后在wordpress站点根目录下,在wp-config.php文件内加入一行

define('WP_CACHE', true);

batcache相关的配置在advanced-cache.php中的batcache类中,查找下面这几行

var $max_age =  3600; // Expire batcache items aged this many seconds (zero to disable batcache)
var $remote  =    0; // Zero disables sending buffers to remote datacenters (req/sec is never sent)
var $times   =    2; // Only batcache a page after it is accessed this many times... (two or more)
var $seconds =  120; // ...in this many seconds (zero to ignore this and use batcache immediately)

在此将缓存有效期设为3600秒,也就是过1小时后缓存将重新生成。在120秒内,连续访问该页面2次将生成缓存。具体数字可以根据实际情况修改。

测试一下,将网站的某个页面刷新几次,在Chrome的开发者工具源码页,注意查看body元素的footer里面,包含了下面一段,表明当前页面是由memached缓存生成的。

<!--
    generated in 0.349 seconds
    28088 bytes batcached for 300 seconds
-->

并发测试

经过几次测试,发现通过这种缓存方案,托管于linode的站点大概最多能承担每秒150个并发请求,再多就超过error rate的阈值了。

未分类

  • 平均响应时间170ms
  • 平均每秒并发数量170个
  • error rate为0.8%
  • 服务器的CPU使用率开始最高90%,后来稳定在50%左右

总结

针对博客类型的小站,最好还是使用fastcgi的方案实现全静态化。如果动态类型较多,可以采用memcached缓存方案,但是并发处理能力没有fastcgi的好。

Memsniff:一款开源的memcached流量分析工具

背景介绍

在知名在线资源存储网站Box上,我们看到云服务已经经历了从一小撮应用服务器和数据库到高规格、高性能协作平台的转变。像大多数大型网络公司一样,Box也依赖于使用分布式缓存层来缓存经常访问的数据。

Box使用memcached(一个高性能的分布式内存对象缓存系统)每天为经常使用的数据对象提供数十万亿请求。然而,我们偶尔也会碰到某些数据对象的访问频率突然变得很高的现象(即出现热键,hot key),热键问题的诱因有很多,有可能是因为后台任务造成的,也有可能是因为应用程序处理不当,又或者是因为用户频繁的活动。

在下图中,我们可以看到几个memcached服务器其中一个网络带宽突然激增的现象(棕色曲线部分表示出现了热键问题)。这种现象会导致数据服务器带宽负载过重,影响缓存服务器提供高性能的服务。

未分类

在此次事件中,很难确定是哪些数据导致了这一问题。因为与数据库不同的是,许多缓存系统为了高效地处理请求,几乎不提供日志,所以很难进行判定。这时就需要一种不同的方法来识别热键。

如今,Box正式推出了memsniff——一款强大的memcached开源流量分析工具。它通过检查memcached服务器上的网络数据包,来分析数据键并提供各个数据键的实时统计信息,包括数据大小、请求速率以及占用的带宽。如此一来,就可以在不影响memcached服务器的情况下识别热键。

Memsniff操作步骤

作为一款强大的、高效和可扩展的开源工具,memsniff的灵感来自于Etsy的mctop和Tumblr的memkeys。它可以在大量流量负载的情况下处理几乎所有的网络数据包(超过99.99%)。此外,它使用golang的简单多线程原语,并在不占用太多CPU或内存的情况下发挥高性能,具体参见下图:

未分类

安装memsniff

memsniff使用了标准的 golang工具链(toolchain),这使得安装过程变得更为便捷。如果你已经安装了golang工具链,并设置了GOPATH环境变量,那么可以通过如下的命令来构建memsniff:

$ go get github.com/box/memsniff

$ go build github.com/box/memsniff

使用memsniff

memsniff需要超级用户权限来捕获大多数操作系统上的网络数据包,-i 参数是必备的,需要用它来指定网卡接口。使用示例:

$ sudo memsniff -i eth0

memsniff还具有从tcpdump的数据包转储文件中读取数据的能力。

$ sudo memsniff -r eth0.pcap

参见memsniff的GitHub主页,了解其他更多的命令行选项。

memsniff的工作原理

未分类

  • 使用 GoPacket 从 libpcap 主线程上捕获原始数据包;

  • 批量的原始数据包被发送到解析工具中,随后,工作人员开始对原始数据包中的memcached协议部分进行解析,来寻找GET请求的响应消息。从中提取返回值的数据键和数据项大小;

  • 提取出来的响应概要被发送给分析工具,然后,根据数据键进行哈希分区,并发送给工作线程。每一个工作线程持有一个分区;

  • 响应来自UI的定期请求,分析工具将各个工作线程的报告合并到单个排序的列表中,并将其展示给UI用户。

memsniff的性能

在一台运行Intel Xeon E5-2470处理器的服务器上,每秒钟可以处理约35万个memcached请求,具体数据如下:

  • 使用了4-5个核(约20个线程,每个CPU使用率约为20%);

  • 100%的数据包处理;

  • 展示99.99%的数据包,表明启动时丢弃了一小部分数据包;

  • 在发生热键问题,网络接口(NIC)出现饱和时,仍然可以处理99.9%的数据包;

  • 使用40MB左右的堆内存(heap);

  • 100MB左右的RSS(可通过GOGC调节);

  • 平均GC停顿:0.6毫秒;

  • 最大GC停顿:2.0毫秒;

memsniff愿景/路线图

我们期待memsniff将以下面的方式进一步发展:

深化功能

  • TCP stream重组:get-miss跟踪、支持二进制协议,支持redis;

  • 触发器(例如,当热键出现时发出警报);

  • 当满足指定条件时自动记录日志到磁盘(例如集成或单个数据键的流量超过阈值);

  • 能够将数据收集限制为与过滤器匹配的数据键;

  • 跟踪单个请求/响应周期;

  • 根据客户端IP限制流量;

改进功能

  • 支持非默认memcached服务器端口;

  • 支持其他替代的排序方式;

  • 支持同时监听多个服务器端口的流量;

  • 支持GET以外的其他操作;

  • 视图过滤;

  • 创建稳定的报告格式,并输出到磁盘;

  • 为其他格式的包(如deb、rpm等)提供构建支持;

使用zabbix监控mariadb性能状态

0x01 前言

zabbix内置Mysql的监控模版,因为mariadb和Mysql两者的相关性,所以这个模版也能用在mariadb services上。

0x02 Mysql

首先要在mariadb新建一个账户,这个账户不需要有任何权限。这个账户只是用来登入mariadb获取服务状态。

GRANT USAGE ON *.* TO 'user name'@'server ip' IDENTIFIED BY 'passwd';
FLUSH PRIVILEGES;

请将以下内容根据实际情况进行替换:

  • user name >>mariadb账户名
  • server ip >>mariadb服务器IP
  • passwd >>mariadb密码

0x03 Zabbix Agent

完成mariadb的用户添加后,还要在mariadb服务器安装Zabbix Agent。通过以下文件可以得知还需要新建一个文件,并在这个文件内填入mariadb的信息:

/etc/zabbix/zabbix_agentd.d/userparameter_mysql.conf 

# For all the following commands HOME should be set to the directory that has .my.cnf file with password information.

# Flexible parameter to grab global variables. On the frontend side, use keys like mysql.status[Com_insert].
# Key syntax is mysql.status[variable].
UserParameter=mysql.status[*],echo "show global status where Variable_name='$1';" | HOME=/var/lib/zabbix mysql -N | awk '{print $$2}'

# Flexible parameter to determine database or table size. On the frontend side, use keys like mysql.size[zabbix,history,data].
# Key syntax is mysql.size[<database>,<table>,<type>].
# Database may be a database name or "all". Default is "all".
# Table may be a table name or "all". Default is "all".
# Type may be "data", "index", "free" or "both". Both is a sum of data and index. Default is "both".
# Database is mandatory if a table is specified. Type may be specified always.
# Returns value in bytes.
# 'sum' on data_length or index_length alone needed when we are getting this information for whole database instead of a single table
UserParameter=mysql.size[*],bash -c 'echo "select sum($(case "$3" in both|"") echo "data_length+index_length";; data|index) echo "$3_length";; free) echo "data_free";; esac)) from information_schema.tables$([[ "$1" = "all" || ! "$1" ]] || echo " where table_schema="$1"")$([[ "$2" = "all" || ! "$2" ]] || echo "and table_name="$2"");" | HOME=/var/lib/zabbix mysql -N'

UserParameter=mysql.ping,HOME=/var/lib/zabbix mysqladmin ping | grep -c alive
UserParameter=mysql.version,mysql -V

然后新建以下文件并进行修改,要注意的是 /var/lib/zabbix 这个路径可能并不存在,需要手动新建:

#新建文件夹
mkdir -p /var/lib/zabbix

#修改文件
vim /var/lib/zabbix/.my.cnf

#填入内容
[mysql]
user=zabbix
password=zabbix
host=127.0.0.1

[mysqladmin]
user=zabbix
password=zabbix
host=127.0.0.1

完成后需要通过以下命令手动重启mariadb services和zabbix agent services:

#重启mariadb
systemctl restart mariadb.service

#重启zabbix agent
systemctl restart zabbix-agent.service

0x04 监控点

完成在mariadb服务器端的配置后就通过zabbix控制页面添加mariadb的监控点了。首先进入mariadb服务器的配置界面:

未分类

0x05 结语

添加过程还是挺简单的,我想这个监控在压力测试的时候会挺有用。以下是我数据库近两天的监控数据:

未分类

数据库带宽使用情况

未分类

数据库操作状况

lvs详细介绍及lvs和keepalived的使用

LVS简介

LVS介绍

LVS是Linux Virtual Server的缩写,意即Linux虚拟服务器,是一个虚拟的服务器集群系统,属于4层负载均衡

ipvs和ipvsadm的关系

我们使用配置LVS的时候,不能直接配置内核中的ipvs,需要使用ipvs的管理工具ipvsadm进行管理  

LVS术语

未分类

LVS转发原理

LVS负载均衡器接受所有入站请求,并根据调度算法决定哪个realserver处理该请求

LVS调度算法

  • 轮询(rr):按照请求顺序轮流分发到后端RS
  • 加权轮询(wrr):权值高的获得的任务更多
  • 最小连接数(lc):动态的将请求建立到连接数较少的RS上
  • 加权最小连接数(wlc):调度器自动询问RS的真实负载情况,并动态的调整权

LVS调度算法生产环境选型

一般的网络服务,如:http、mail、MySQL等,常用的调度算法为:

  • 基本轮询调度rr算法
  • 加权轮询调度wrr算法
  • 加权最小连接调度wlc算法

LVS转发模式

  • NAT(Network Address Translation)
  • DR(Direct Routing)
  • TUN

LVS-DR模式

转发流程

将所有入站请求转发给后端realserver,后端realserver处理完直接将结果发给客户端

未分类

原理

当用户请求到达Direct Server,此时报文的源IP为CIP、MAC为CIP-MAC,目标IP为VIP、MAC为VIP-MAC
Direct Server根据调度算法确定一台处理请求的realserver,将请求转发给对应的realserver,此时源IP和目标IP均未改变,仅修改了源MAC为DIP-MAC,目标MAC为RIP-MAC
对应的realserver处理完请求,直接将结果发给客户端,此时源IP为VIP、MAC为VIP-MAC,目标IP为CIP、MAC为CIP-MAC

特性

  • 通过在调度器上修改数据包的目的MAC地址实现转发
  • Real-Server和Direct-Server必须在同一网段
  • Real-Server的lo接口必须绑定VIP

为什么要抑制ARP请求

  • 由于后端Real-Server要将VIP绑定到lo网卡上,这就出现了一个问题,客户端请求到达LVS前端路由器的时候,前端路由器会发送一个{目标地址为VIP}的请求报文,所以需要抑制Real-Server的ARP,保证让Direct-Server收到这个报文,而不是realserver收到这个报文

  • 一句话说明抑制Real-Server原因:保证前端路由将目标地址为VIP的报文发给Direct-Server,而不是Real-Server

优势

只有请求报文经过调度器,而Real-Server响应处理后无需经过调度器,因此并发量大的时候效率很高

LVS-NAT模式

转发流程

将所有入站请求转发给后端Real-Server,后端Real-Server处理完再发给Direct-Server,Direct-Server再发给客户端

特性

  • 既有RIP也有VIP
  • Real-Server必须得跟Direct-Server在同一网段
  • Real-Server必须将网关指向Direct-Server

优势

只需要一个公网IP给Direct-Server,Direct-Server始终跟外接打交道

劣势

需要依赖Direct-Server把请求转发给Real-Server,Real-Server处理完把结果发给Direct-Server,Direct-Server再把结果转发出去,并发高的时候会成为瓶颈

LVS三种模式对比

未分类

ipvsadm介绍

ipvsadm参数

添加虚拟服务器
    语法:ipvsadm -A [-t|u|f]  [vip_addr:port]  [-s:指定算法]
    -A:添加
    -t:TCP协议
    -u:UDP协议
    -f:防火墙标记
    -D:删除虚拟服务器记录
    -E:修改虚拟服务器记录
    -C:清空所有记录
    -L:查看
添加后端RealServer
    语法:ipvsadm -a [-t|u|f] [vip_addr:port] [-r ip_addr] [-g|i|m] [-w 指定权重]
    -a:添加
    -t:TCP协议
    -u:UDP协议
    -f:防火墙标记
    -r:指定后端realserver的IP
    -g:DR模式
    -i:TUN模式
    -m:NAT模式
    -w:指定权重
    -d:删除realserver记录
    -e:修改realserver记录
    -l:查看
通用:
    ipvsadm -ln:查看规则
    service ipvsadm save:保存规则

ipvsadm配置LVS负载均衡

需求

用LVS实现后端两台httpd的负载均衡

环境说明

未分类

负载均衡器端

安装LVS
    [root@lb01 ~]#yum -y install ipvsadm 
    [root@lb01 ~]#ipvsadm  
添加绑定VIP
    [root@lb01 ~]#ip addr add 192.168.0.89/24 dev eth0 label eth0:1
配置LVS-DR模式
    [root@lb01 ~]#ipvsadm -A -t 192.168.0.89:80 -s rr
    [root@lb01 ~]#ipvsadm -a -t 192.168.0.89:80 -r 192.168.0.93 -g 
    [root@lb01 ~]#ipvsadm -a -t 192.168.0.89:80 -r 192.168.0.94 -g

Real-Server端

配置测试后端realserver
    配置httpd省略
    [root@realserver-1 ~]#curl 192.168.0.93 #测试realserver-1网站是否正常    
    192.168.0.93
    [root@realserver-2 ~]#curl 192.168.0.94 #测试realserver-2网站是否正常
    192.168.0.94
绑定VIP到lo网卡
    [root@realserver-1 ~]#ip addr add 192.168.0.89/32 dev lo label lo:1  #由于DR模式需要realserver也有VIP
抑制ARP
    [root@realserver-1 ~]#echo 2 > /proc/sys/net/ipv4/conf/all/arp_announce  
    [root@realserver-1 ~]#echo 2 > /proc/sys/net/ipv4/conf/lo/arp_announce
    [root@realserver-1 ~]#echo 1 > /proc/sys/net/ipv4/conf/all/arp_ignore
    [root@realserver-1 ~]#echo 1 >/proc/sys/net/ipv4/conf/lo/arp_ignore  

客户端测试

[root@test ~]#curl 192.168.0.89
192.168.0.93
[root@test ~]#curl 192.168.0.89
192.168.0.94

配置LVS+keepalived

需求

  • LVS给两台httpd做负载均衡
  • keepalived做lvs高可用,同时做Real-Server健康检查,如果发现Real-Server80端口没开,就认为故障,从集群中剔除
  • 在keepalived配置文件内就能配置LVS  

环境说明

未分类

在负载均衡器端配置lvs+keepalived

lb01节点

[root@lb01 ~]# vim /etc/keepalived/keepalived.conf
! Configuration File for keepalived
##################全局配置##########################
global_defs {
    #如有故障,发邮件地址
    notification_email {      
    [email protected]              #收件人
    }
    notification_email_from [email protected]  #keepalived报警邮件,发件人
     smtp_server 192.168.200.1  #邮件服务器地址
    smtp_connect_timeout 30     #邮件服务器超时时间
    router_id LVS_01             #类似于MySQL的server-id,每个keepalived节点不能相同
}
#################keepalived配置#####################
vrrp_instance VI_1 {
    state MASTER              #keepalived角色,MASTER和BACKUP
    interface eth0            #通信接口,下面的virtual_ipaddress(VIP)绑定到这个网卡
    virtual_router_id 51      #vrrp_instance的唯一标识
    priority 150              #keepalived权重,数值越大权重越大,MASTER应大于BACKUP
    advert_int 1              #发送心跳间隔,如果backup1秒收不到心跳就接管,单位是秒
    authentication {          #每个keepalived节点通过这里设置的验证通信,必须得设置成一样
        auth_type PASS
        auth_pass 1111
    }
    virtual_ipaddress {
        192.168.0.89/24       #VIP
    }
}
##################LVS配置##############            
#添加虚拟服务器
#相当于 ipvsadm -A -t 192.168.0.89:80 -s wrr 
virtual_server 192.168.0.89 80 {
    delay_loop 6             #服务健康检查周期,单位是秒
    lb_algo wrr                 #调度算法
    lb_kind DR                 #模式
    nat_mask 255.255.255.0   
    persistence_timeout 50   #回话保持时间,单位是秒
    protocol TCP             #TCP协议转发
#添加后端realserver
#相当于 ipvsadm -a -t 192.168.0.89:80 -r 192.168.0.93:80 -w 1
    real_server 192.168.0.93 80  {    #realserver的真实IP
        weight 1                      #权重
        #健康检查
        TCP_CHECK {
            connect_timeout 8         #超时时间
            nb_get_retry 3            #重试次数
            delay_before_retry 3      #重试间隔
            connect_port 80           #检查realserver的80端口,如果80端口没监听,就会从集群中剔除
        }
    }
    real_server 192.168.0.94 80  {
        weight 1
        TCP_CHECK {
           connect_timeout 8
           nb_get_retry 3
           delay_before_retry 3
           connect_port 80
        }
    }

}

lb02节点

[root@lb01 ~]# vim /etc/keepalived/keepalived.conf
! Configuration File for keepalived
################全局配置###########################
global_defs {
    notification_email {      
    }
    notification_email_from [email protected]  
    smtp_server 192.168.200.1
    smtp_connect_timeout 30
        router_id LVS_02        
}
################keepalived配置#####################
vrrp_instance VI_1 {
    state BACKUP             
    interface eth0         
    virtual_router_id 51      
    priority 100             
    advert_int 1             
    authentication {         
        auth_type PASS
        auth_pass 1111
    }
    virtual_ipaddress {
        192.168.0.89/24
    }
}
################lvs配置##########################
virtual_server 192.168.0.89 80 {
    delay_loop 6
    lb_algo wrr             
    lb_kind DR                 
    nat_mask 255.255.255.0
    persistence_timeout 50    
    protocol TCP              
    real_server 192.168.0.93 80  {
        weight 1             
        TCP_CHECK {            
            connect_timeout 8   
            nb_get_retry 3        
            delay_before_retry 3  
             connect_port 80      
        }
    }
    real_server 192.168.0.94 80  {
        weight 1
        TCP_CHECK {
         connect_timeout 8
         nb_get_retry 3
         delay_before_retry 3
         connect_port 80
        }
    }

                }

配置后端Real-Server

确保网站服务是正常的
    curl 192.168.0.93
    192.168.0.93
    curl 192.168.0.94
    192.168.0.94
绑定VIP到lo网卡
    ip addr add 192.168.0.89/32 dev lo label lo:0
抑制ARP
    [root@realserver-1 ~]#echo 2 > /proc/sys/net/ipv4/conf/all/arp_announce  
    [root@realserver-1 ~]#echo 2 > /proc/sys/net/ipv4/conf/lo/arp_announce
    [root@realserver-1 ~]#echo 1 > /proc/sys/net/ipv4/conf/all/arp_ignore
    [root@realserver-1 ~]#echo 1 >/proc/sys/net/ipv4/conf/lo/arp_ignore  

客户端测试

  • 负载均衡是否正常
  • 后端Real-Server出问题是否自动剔除
  • lvs高可用是否正常,提供服务的LVS宕机,vip漂移到另一台LVS继续提供服务

一次Mysql改表引发LVS下RS机器全下线的问题

某天下午,正在和code苦战的我突然收到报警短信,告警我们有个业务电信机房LVS下的RS机器全部下线了。第一时间去看机器负载情况,发现CPU IDLE在80%左右,其他各项指标也都正常;怀疑是LVS的KeepAlive程序出问题了,上管理平台点了一遍RS上线,发现服务恢复了,于是未做进一步排查,只向OPS同学反馈了一下。

然而,刚过了半个小时,同样的报警又来了,看来还得找到根本原因。挑了一台机器保留现场,并在管理平台将其他机器操作上线,以保证充分的排查时间。

先检查Nginx allweb.log中lvscheck相关的日志,发现状态码全部为499且request_time达到5s:

[tabalt@server01 ~]$ tail -100 /data/nginx/logs/allweb.log | grep lvscheck
10.18.42.2 92 0 5.000[s] - - [12/Jul/2017:18:29:18 +0800] "GET /status.php HTTP/1.0" 499 - "-" "KeepAliveClient" lvscheck.domain.com 10.20.12.60 - -
10.18.42.2 92 0 5.000[s] - - [12/Jul/2017:18:29:22 +0800] "GET /status.php HTTP/1.0" 499 - "-" "KeepAliveClient" lvscheck.domain.com 10.20.12.60 - -
10.18.42.2 92 0 5.000[s] - - [12/Jul/2017:18:29:24 +0800] "GET /status.php HTTP/1.0" 499 - "-" "KeepAliveClient" lvscheck.domain.com 10.20.12.60 - -
...

原来KeepAlive程序请求http://lvscheck.domain.com/status.php页面探测服务情况时,竟然过了5s都没有收到响应,于是主动断开请求并将RS下线了。但机器很闲,为什么/status.php会处理超过5s呢?

检查PHP-FPM的日志,发现有报错/data/www/front/index.php文件执行很慢:

[tabalt@server01 ~]$ tail /data/php/log/php-fpm.log
12-Jul-2017 18:29:18] WARNING: [pool www] child 3988, script '/data/www/front/index.php' (request: "GET /index.php") executing too slow (11.301960 sec), logging
[12-Jul-2017 18:29:22] WARNING: [pool www] child 3945, script '/data/www/front/index.php' (request: "GET /index.php") executing too slow (11.863325 sec), logging
[12-Jul-2017 18:29:24] WARNING: [pool www] child 3887, script '/data/www/front/index.php' (request: "GET /index.php") executing too slow (10.498795 sec), logging
...

但/data/www/front/index.php只是入口文件,从这个日志看不出来问题在哪里,再检查下PHP-FPM的慢日志:

[tabalt@server01 ~]$ tail -100 /data/php/log/www.log.slow
...
script_filename = /data/www/front/index.php
[0x00007fecbd613f90] execute() /data/www/vendor/andals/vine/src/Component/Mysql/Driver.php:218
[0x00007fecbd613ec0] doExecute() /data/www/vendor/andals/vine/src/Component/Mysql/Driver.php:66
[0x00007fecbd613df0] query() /data/www/vendor/andals/vine/src/Component/Mysql/Dao/Base.php:206
[0x00007fecbd613d80] simpleQuery() /data/www/src/app/Model/Dao/Base.php:65
[0x00007fecbd613cc0] selectByParamsForFront() /data/www/src/app/Model/Svc/SqlBase.php:211
[0x00007fecbd613c10] selectByParamsForFront() /data/www/src/app/Model/Svc/Category.php:214
...
[0x00007fecbd613580] getEsData() /data/www/src/app/Controller/Front/ListController.php:26
[0x00007fecbd613400] indexAction() /data/www/vendor/andals/vine/src/Framework/App/Web.php:107
[0x00007fecbd613380] call_user_func_array() /data/www/vendor/andals/vine/src/Framework/App/Web.php:107
[0x00007fecbd613290] runController() /data/www/vendor/andals/vine/src/Framework/App/Web.php:73
[0x00007fecbd6131b0] handleRequest() /data/www/vendor/andals/vine/src/Framework/App/Web.php:48
[0x00007fecbd6130f0] run() /data/www/src/run/front/index.php:6

可以看到最终是执行SQL的时候很慢,上管理平台查看发现在报警的两个时间点,MySQL从库的QPS突然降到0而主库QPS突然大幅升高,于是连忙反馈给DBA同学。

DBA同学排查后发现,当前读写量比较大,且有个新增字段的改标语句正在运行,停止后问题恢复;而主从库QPS的突变是因为从库延时大被Proxy操作下线了。

我们梳理后发现,当前有个Task程序在批量往数据库里导数据,表里的数据较多(千万级),这种情况下改表导致数据库响应变慢;同时页面上有个查询没有加缓存,SQL语句执行超时设置得也有问题,最终导致PHP-FPM进程都被卡住了,没有空闲进程来处理LVS健康检查的页面,出现了LVS下RS机器全下线的问题。

事后,我们对发现的问题做了修复,并在确保没有大量访问的情况下提交了改表操作,改表顺利执行完成。

wok和kimchi – kvm虚拟机网页管理

先安装wokd服务

1、解决依赖

yum install gcc make autoconf automake gettext-devel git rpm-build libxslt 
python-cherrypy python-cheetah PyPAM m2crypto 
python-jsonschema python-psutil python-ldap 
python-lxml nginx openssl python-websockify 
fontawesome-fonts logrotate python-ordereddict

2、下载rpm包

wget https://github.com/kimchi-project/wok/releases/download/2.5.0/wok-2.5.0-0.el7.centos.noarch.rpm

3、安装wokd

rpm -ivh wok-2.5.0-0.el7.centos.noarch.rpm

4、启动wokd服务

systemctl daemon-reload
systemctl start wokd

安装kimchi

1、解决依赖

yum install gcc make autoconf automake gettext-devel git rpm-build libxslt 
libvirt-python libvirt libvirt-daemon-config-network 
qemu-kvm python-ethtool sos python-ipaddr nfs-utils 
iscsi-initiator-utils pyparted python-libguestfs 
libguestfs-tools novnc spice-html5 
python-configobj python-magic python-paramiko 
python-pillow python-ordereddict

重启libvirtd服务:

systemctl restart libvirtd

2、下载rpm包

wget https://github.com/kimchi-project/kimchi/releases/download/2.5.0/kimchi-2.5.0-0.el7.centos.noarch.rpm

3、安装kimchi

rpm -ivh kimchi-2.5.0-0.el7.centos.noarch.rpm

启动服务:

关闭selinux:

setenforce 0

重启wokd服务:

systemctl restart wokd

启动nginx服务:

systemctl start nginx

关闭网络自动管理

systemctl stop NetworkManager

通过浏览器访问:

https://IP_ADDR:8001

wokd默认通过PAM方式来验证,因此可以直接使用主机上的用户账号登录。