深入理解GlusterFS之数据均衡

http://blog.itpub.net/31547898/viewspace-2168800/

GlusterFS是一个免费的开源分布式文件系统,具有无中心节点、堆栈式设计、全局统一命名空间、高可用、高性能和横向扩展等特点,在业界被广泛使用。本文主要介绍GlusterFS的数据均衡功能(即rebalance),内容涉及数据均衡的产生背景、使用场景、基本原理、程序实现剖析、操作命令实践、存在的问题以及需要优化的地方等,希望能够抛砖引玉,为读者深入学习和理解GlusterFS起到一定的参考作用。

本文假定读者已经熟悉了GlusterFS的一些基本概念术语以及架构组成。

1. 问题引入

1.1.    问题背景

在当今大数据时代,人们每天都在产生大量的数据,其中需要持久化的数据也越来越多,而一台计算机的存储容量是比较有限的,尽管可以为其增加更多的内存和磁盘,但再怎么增加也是有上限的,因此分布式存储系统便应运而生。分布式存储系统就是利用多个独立的计算机来解决单个计算机无法解决的存储数据量大的问题,对于这种系统而言,扩容(增加节点)和缩容(减少节点)是不可避免会遇到的情况,GlusterFS、Ceph、HDFS和OpenStack Swift等分布式存储系统均如此。

以扩容为例,这是一个十分常见的场景,随着数据量的增多,必然要对系统进行扩容来满足实际需求,而扩容系统后会出现集群内数据分布不均衡的情况,已有数据只存在原有节点上,新增节点只会存储后续新增的数据,这样发展下去会造成原有节点负载过高,而新增节点可能还有很多可用空间,这就产生了不均衡(如图1),为了解决该问题,很多分布式存储系统都支持数据均衡(即rebalance)功能,GlusterFS、Ceph、HDFS和Swift等也是如此,执行数据均衡后,可以使集群中的数据进行重新分布,并且分布的更加均匀(如图2)。

图1 扩容后数据均衡前示意图

图2 扩容后数据均衡后示意图

本文主要讨论GlusterFS数据均衡,接下来先介绍下GlusterFS数据均衡涉及到的相关内容,尤其是DHT(Distributed Hash Table)部分,因为数据均衡和DHT结合的非常紧密。

GlusterFS使用DHT模块来聚合多台机器的物理存储空间,形成一个单一的全局命名空间,并使用卷(Volume)这一逻辑概念来表示这样的空间。每个卷可以包含一个或多个子卷(Subvolume),子卷也可称为DHT子卷,同样是一个逻辑概念,一个子卷可以是单个brick、一个副本卷(Replica)或一个EC(Erasure Coding)卷,而副本卷和EC卷自身又都是由一组brick构成。而brick则是GlusterFS中的最基本存储单元,表示为一个机器上的本地文件系统导出目录。例如,GlusterFS分布式副本卷(双副本)的简化组成如图3所示。

图3 GlusterFS分布式副本卷示意图

DHT模块使用基于32位哈希空间的一致性哈希算法(Davies-Meyer算法)计算文件的哈希值,并将文件存储到其中一个DHT子卷,而目录是GlusterFS中哈希分布(layout)的基本单位,会在所有DHT子卷中都创建,哈希范围保存在目录的扩展属性中。根据DHT算法原理,每一个DHT子卷的目录都会被分配一个哈希子空间,即32位哈希空间(十六进制表示为0x00000000~0xffffffff)中的一段哈希范围,例如,0x00000000~0x55555554。

为了在集群内均衡地分布文件,GlusterFS在每个目录层次上重新划分一次哈希空间,并且子目录层和父目录层的哈希分布并无关联。而文件的实际存储位置,只由其父目录上的哈希范围决定,与其他目录层次无关。子卷的目录哈希空间分布示意图如图4所示。

图4 GlusterFS卷目录哈希空间分布示意图

根据以上介绍,可以概括GlusterFS中文件访问的一般流程如下:

1、首先,DHT根据文件名称计算出一个哈希值(假设图4中file1哈希值为0x23333333);

2、其次,根据该哈希值找到其所在的目录哈希子空间(即0x00000000~0x55555554),也就是找到了对应的DHT子卷(即subvolume1);

3、最后,访问对应的DHT子卷(即subvolume1)。

1.2.    使用场景

理想情况下,在一个GlusterFS卷中,会尽可能地在DHT子卷之间均衡地存储文件,这样可以充分发挥GlusterFS的高可靠性、高可用性和高性能。但现实中总有一些特殊情况存在,比如,集群需要扩容等,从而导致文件分布不均匀,因此也就需要进行数据均衡。

那么GlusterFS中数据均衡具体指的是什么呢?简而言之,GlusterFS数据均衡就是必要的时候,在DHT各子卷之间迁移数据的过程,其目的是使数据在集群中的不同节点之间尽量均匀分布,从而使得集群处于最佳状态。

那么问题来了,何时才是数据均衡的“必要的时候”呢?主要有如下两类场景:

(一)扩容或缩容文件系统

(二)重命名文件

1.2.1 扩容或缩容

扩容或缩容GlusterFS按照子卷为单位来做增减,这会使得DHT子卷的数量发生变化,从而导致每个子卷的目录哈希范围会被重新计算和分配,即每个子卷的目录哈希范围会改变。而文件的哈希值并没有变化,如果此时有一些文件的哈希值落到了其他子卷(即不同于文件当前所在子卷),那么这些文件应该被迁移到正确的子卷。

值得注意的是,在扩容GlusterFS后,需要手动执行gluster rebalance命令来触发数据均衡功能。前文已经介绍了,扩容后会带来新旧节点数据不均衡的问题,进一步发展可能会导致旧节点负载过高而出现性能问题,甚至最终影响到数据可靠性和可用性,因此,扩容后进行数据均衡是非常必要的。

而在缩容GlusterFS后,并不需要手动执行命令,缩容时会自动触发执行数据均衡过程,这是因为如果缩容时没有自动进行数据均衡,那么被剔除掉的节点或子卷上的数据将不再可用,从而会导致数据的丢失,这对于用户来说是不可接受的,因此数据均衡在缩容时是不可或缺的,程序实现采用自动触发方式也就理所当然了。

下面分别给出扩容和缩容后,并且执行数据均衡后的子卷目录哈希空间分布变化示意图,如图5和图6所示。

图5 扩容后子卷的目录哈希空间分布变化

图6 缩容后子卷的目录哈希空间分布变化

1.2.2.      重命名文件

在GlusterFS中,重命名文件会导致该文件的哈希值发生变化,假设此时DHT子卷的数量并没有变化,即没有扩容和缩容,那么每个子卷的目录哈希范围也就没有变化,根据DHT算法判断该文件的新存储位置,如果文件的新存储位置与当前所在子卷不同(如图7),则该文件应该被迁移到正确的子卷。

重命名文件后,系统不会自动进行均衡,而是会在目标子卷上产生一个同名的内容为空的链接文件(Link file),该链接文件的扩展属性上会记录文件的实际存储位置(也就是原来所在的子卷)。假设此时客户端访问重命名后的文件,根据前面介绍的文件访问流程,则DHT会先将请求转到哈希计算得出的子卷去查找该文件,并获取到链接文件信息,DHT模块懂得链接文件的意义,从链接文件信息中得出文件的实际位置,然后再到实际的子卷获取文件。

可以发现,如果重命名文件后不进行数据均衡,则客户应用程序在访问文件时会增加额外的步骤,从而造成一定程度的访问延迟,当系统有大量链接文件时,则会导致访问性能的大幅下降,对应用程序造成影响。而执行数据均衡后则会将文件迁移到正确的位置,消除了链接文件带来的访问延迟问题,因此数据均衡对于文件重命名来说也是很有必要的。

图7 重命名文件导致的文件位置变化

2. 基本原理

前面简要介绍了DHT模块的基本作用和文件的一般访问流程,目的是为了读者能够更好地理解GlusterFS数据均衡。本节将介绍当前数据均衡功能是如何工作的。

随着GlusterFS项目的发展,其数据均衡功能也在不断的完善,其最初的工作机制和现在的工作机制已经相去甚远,下面高度概括了数据均衡当前的流程,如图8所示:

图8 数据均衡的概括流程

为了便于理解,以上流程是站在集群的视角,对数据均衡流程的高度总结,具体如下:

1、扫描整个GlusterFS卷,获取到卷中所有目录和文件;

2、对每一个目录,修复该目录的哈希分布;

3、对每一个文件,判断该文件是否需要迁移;

4、对于需要迁移的文件,将其迁移到正确的位置。

 

可以看到,概括总结后的流程非常简单,但是实际代码实现却比较复杂,一方面是考虑了各种异常情况后,会有大量的细节需要处理,另一方面是数据均衡功能的代码与DHT的代码结合的非常紧密,从而增加了代码的复杂度,不便于理解。后面的实现剖析部分会具体介绍代码级别的处理流程。

本节接下来先介绍数据均衡功能稍微细化后的工作机制,然后再给出一个实例分析,用于帮助读者理解。

2.1.    工作机制

在GlusterFS的数据均衡功能实现中,每个节点采用单进程多线程的实现方式,其中,主线程使用类似深度优先算法,从根目录开始,遍历GlusterFS卷在本节点上的目录并修复其哈希分布,同时爬取目录下的所有文件,根据算法将相应文件放到迁移队列里,并通知等待的工作线程进行迁移处理。

而工作线程则负责检查迁移队列里是否有文件待迁移,若队列不空则迁移其中的文件,一次迁移一个文件;若队列为空则自我睡眠,等待主线程唤醒。

主线程的文件爬取工作和工作线程的数据迁移工作同时进行,并且支持多个线程并行迁移文件。当主线程完成修复目录和爬取文件工作后,将等待其他工作线程完成数据迁移工作,当迁移队列里的所有文件都被迁到正确位置后,所有工作线程结束退出,主线程收到所有工作线程的退出消息后,做一些清理工作,然后结束本节点的数据均衡进程。

下面给出稍微细化后的数据均衡主线程和迁移线程的工作流程如图9和图10所示。

图9 数据均衡主线程的工作机制

启动数据均衡功能后,每个节点的数据均衡进程的主线程都按照上图流程进行,并且每个节点只处理属于本地brick上的文件,上述流程简要说明如下:

1、首先,修复卷的根目录哈希分布;

2、打开并遍历当前目录,获取到所有子目录;

3、对于当前目录中的每个子目录,按照如下步骤递归处理:

3.1、如果有未被处理的子目录,从中选择一个目录,并返回到步骤2处理该子目录;否则,下一步;

3.2、所有子目录都已经被处理过了或者没有子目录,修复当前目录的哈希分布;

3.2.1、打开并遍历当前目录,获取所有文件;

3.2.2、对于当前目录中的每个文件,按照如下步骤处理:

3.2.2.1、如果有未被处理的文件,从中选择一个文件,进入下一步,否则,转到步骤3.2.2.4;

3.2.2.2、如果文件应该由本节点处理,则进入下一步,否则,转到步骤3.2.2;

3.2.2.3、如果文件满足迁移条件,则将文件放到一个迁移队列,并通知迁移线程执行迁移,否则,返回步骤3.2.2;

3.2.2.4、当前目录的所有文件已被处理过了,转入下一步;

4、如果本节点还有目录的哈希分布未被修复,选择其中的一个目录(按照之前遍历时的顺序),并转到步骤3,否则,进入下一步;

5、等待所有迁移线程完成数据迁移工作,然后做些清理工作,最后结束数据均衡进程。

工作线程的处理流程如下:

图10 数据均衡迁移线程的工作机制

数据均衡的迁移线程处理流程简介如下:

1、获取到数据均衡进程的迁移队列;

2、检查队列是否为空;

2.1、如果队列不空,则从队列头部取出一个文件,并处理该文件的具体迁移过程,迁移完成后返回步骤2;

2.2、如果队列为空,则继续判断主线程是否已经完成对整个卷的文件爬取;

2.2.1、如果主线程未完成文件爬取,则迁移线程进入休眠状态,等待主线程的唤醒;

2.2.2、如果主线程已完成文件爬取,则退出线程。

2.2.    实例分析

以上内容可能有些晦涩难懂,为了便于理解,下面举个例子,假设GlusterFS卷中的目录文件布局如图11所示:

图11 GlusterFS卷的目录文件布局示意图

根据上述数据均衡的工作机制,其处理顺序如下:

1、数据均衡进程启动,主线程首先修复根目录的哈希分布;

2、打开并遍历根目录,获取到文件file1、目录dir1和dir2;

3、选择根目录的一个子目录,假设选择dir1;

4、打开并遍历dir1,获取到文件file2、目录dir3和dir4;

5、选择dir1目录的一个子目录,假设选择dir4;

6、打开并遍历dir4,获取到文件file5;

7、因为dir4没有子目录,所以不再进行递归遍历,而是修复dir4的哈希分布;

8、遍历dir4,获取其下所有文件,本例为file5;

9、判断file5是否应该由本节点处理,假设由本节点处理;

10、判断file5是否需要迁移,假设需要迁移;

11、将file5放到迁移队列,通知工作线程进行迁移(迁移线程负责处理具体的迁移过程);

12、dir4目录下没有其他文件需要处理了,即对dir4的处理完成了,按照之前的遍历顺序,继续处理下一个哈希分布未修复的目录,也就是优先选择与dir4同一层次的其他目录;

13、选择dir3,由于dir3也没有子目录,修复dir3的哈希分布;

14、打开并遍历dir3,获取到文件file4,然后同file5的处理流程一样,不再赘述;

15、处理完dir3后,则当前同一层次的目录都处理完了(即dir3和dir4),开始处理当前目录层次的父目录,即dir1;

16、完成对dir1的修复目录、遍历文件和迁移文件,具体步骤同dir4,不再赘述;

17、接着上面的步骤3,对dir1的同一层次的其他目录(本例为dir2)进行处理,过程与dir1类似,即先修复dir5哈希分布,迁移其下文件file6,再修复dir2哈希分布,迁移文件file3,具体过程不再赘述;

18、最后处理根目录的文件file1,不再赘述;

19、数据均衡进程等待所有迁移线程完成数据迁移,然后正常结束。

以上处理顺序的简化示意图如下图12所示:

图12 目录处理与文件迁移顺序

总结一下,数据均衡的当前工作机制就是多个节点同时参与处理,每个节点以单进程多线程的方式,同时扫描文件和迁移文件,并且扫描文件和迁移文件分别由不同线程处理,每个节点可以并行迁移文件,相比以往的数据均衡流程,大大增加了并行度,并且更加可扩展,使得集群数据均衡时的系统负载分布的更加均匀,同时效率也更高了。

3. 最佳实践

GlusterFS为数据均衡功能提供了相应的命令行程序,通过该程序可以和glusterd服务进程之间进行通信,用于查询数据均衡状态信息和控制数据均衡相关操作,可以辅助相关人员更好地控制管理数据均衡过程。

3.1.    操作命令

命令格式为:

gluster volume rebalance <VOLNAME> {{fix-layout start} | {start [force]|stop|status}}

常用命令介绍如下:

(1)只修复目录layout命令

gluster v rebalance VOL_NAME fix-layout start

例如:

 

(2)开启数据均衡命令

gluster v rebalance VOL_NAME start

例如:

 

也可以使用强制选项,这样可以忽略一个容量限制,即不带强制选项时会比较文件所在的原子卷和目标子卷的剩余容量,如果原子卷大于目标子卷,则不迁移该文件。使用强制选项则会跳过这个限制,命令如下:

gluster v rebalance VOL_NAME start force

例如:

 

(3)设置数据均衡迁移速度

为了良好的迁移性能,数据均衡进程中使用了多线程,并且支持并行迁移多个文件,但是这样占用一定的系统资源,对存储系统本身性能带来一定影响,因此,GlusterFS提供了相关控制命令,用于调控实际迁移速度的快慢,目前主要有三种模式,lazy、normal和aggressive。顾名思义,lazy是懒惰的慢速模式(较少线程迁移),normal是正常模式(线程数量适中),aggressive是激进模式(较多线程迁移),默认采用normal模式,可以根据实际需要去做具体设置,命令格式如下:

gluster volume set <VOLNAME> rebal-throttle lazy|normal|aggressive

例如:

 

(4)查看数据均衡状态等信息

gluster v rebalance VOL_NAME status

例如:

 

可以查看到数据均衡在每个节点的当前状态(status)、运行了多久(run time)、扫面的文件数量(scanned)、已经迁移的文件数量(rebalanced-files)、已经迁移的数据量(size)、迁移失败的文件数量(failures)和跳过的文件数量(skipped)。

(5)停止数据均衡进程

gluster v rebalance VOL_NAME stop

例如:

 

3.2.    均衡建议

当集群需要进行数据均衡时,建议参考如下内容:

(1)尽量提前做规划,例如,别等到集群存储空间快用完了才扩容,一方面会导致时间紧迫,部署准备时间匆忙,容易忙中出错;另一方面也容易导致旧节点之间的文件迁移失败,最好预留出一定的剩余空间;

(2)确保集群所有节点处于正常状态,卷处于启动状态,glusterd服务进程和brick进程状态正常,节点之间通信正常;

(3)检查GlusterFS卷中是否有文件损坏,如果有,则先对其进行修复;

(4)在执行数据均衡时,确保集群没有自修复操作正在进行,否则会影响到数据正确性和迁移效率;

(5)如果允许的话,在执行数据均衡前,停止客户端应用,可以提高均衡效率;

(6)先执行fix-layout操作,再执行数据迁移,可以在一定程度上提高迁移效率;

(7)根据实际需要选择迁移模式,默认是normal模式,aggressive模式可能占用的系统资源较多,进而影响到存储性能;

(8)数据均衡过程中,通过命令行程序定时关注均衡的当前状态,以便及时发现问题并做相应调整;

(9)当集群规模较大时,可能偶尔会出现某个节点均衡失败的情况,一般重新开始执行均衡即可;

(10)如果执行数据迁移对应用程序影响较大,可以只执行fix layout,这样可以只修复目录的哈希分布,并不会实际迁移文件,此时新文件可以存储到新增节点(或brick)上,之后再找适当时机(系统比较空闲的时候)执行数据迁移操作。

4. 实现剖析

GlusterFS数据均衡功能的代码与DHT层代码紧密结合在一起,主要的功能逻辑代码位于dht-rebalance.c文件中,剩余部分代码包含在DHT层的其他代码逻辑中。在数据均衡代码的实现中,采用了syncop框架,这是一套基于协程的合作式多任务同步框架,它的存在简化了数据均衡功能的实现。

前面的工作机制部分介绍了数据均衡的基本流程,本节则主要侧重代码级别的讨论,首先简要介绍数据均衡功能中涉及的进程交互,然后讨论syncop框架,最后给出数据均衡的几个关键流程。限于时间原因,忽略了很多代码处理细节,留给读者自行分析实践。

4.1.    进程交互过程

下面以命令行手动触发数据均衡为例,简要说明其中涉及的进程交互步骤,在命令执行的节点上,简化的进程交互过程如下图13所示。

 

图13手动触发数据均衡的进程交互过程

交互过程主要涉及到cli、glusterd和glusterfs三个进程,简要说明如下:

1、在集群的一个节点上,执行gluster rebalance start命令(即启动cli进程),该命令向本节点glusterd服务进程发起rpc连接建立请求;

2、本节点glusterd进程接收到该请求,与之建立rpc连接并回复响应;

3、cli命令进程收到rpc连接建立响应,然后向本节点glusterd进程发起启动数据均衡请求;

4、本节点glusterd进程收到请求后,做一些初始化等准备工作,并以事务方式向集群其他相关节点(当前GlusterFS卷的所有其他节点)的glusterd进程,发起启动数据均衡的请求;

5、与此同时,本节点glusterd进程启动本节点的数据均衡进程;

6、数据均衡进程以gluster客户端进程(即glusterfs)的形式启动,该进程使用的客户端配置文件(即.vol文件)与正常客户端配置文件不同,去掉了其中的性能相关模块;

7、本节点的数据均衡进程启动完毕后,返回响应给glusterd进程;

8、本节点glusterd进程等待并接收所有其他相关节点的响应,即数据均衡进程已经启动完毕的消息;

9、本节点glusterd进程返回启动数据均衡的响应,通知命令行程序启动已经完成;

10、命令行程序从本地glusterd进程接收响应,打印输出命令执行结果,然后退出cli程序;

11、本节点glusterd进程完成了对命令行请求的处理,等待其他请求到来;

12、本节点数据均衡进程开始处理,具体处理流程参见前文中的工作原理部分,均衡进程在处理完成后退出。

4.2.    syncop框架

在GlusterFS的几乎所有线程中,文件操作(fops)都是异步执行的,使用的基本原语是STACK_WIND和STACK_UNWIND这一类宏定义,这类宏定义像调用函数和回调函数一样工作,有效地实现了集群和并行环境下的文件操作。

但在数据均衡功能中,如果直接采用这类宏定义实现相关操作,则会使程序变得非常复杂而不好控制,因此,GlusterFS开发人员引入了syncop同步框架,并广泛使用于程序中,以方便数据均衡功能的实现,例如,用于文件爬取程序和流程控制等方面。

4.2.1.      syncop术语

  • syncenv

Syncenv是同步环境对象,该对象拥有一个可调度的工作线程池,同步任务(synctask)在该环境中调度执行。

  • synctask

Synctask是同步任务对象,可以理解为一对C语言函数指针,即调用函数指针synctask_fn_t(如syncop_lookup)和回调函数指针synctask_cbk_t(如syncop_lookup_cbk)。

Synctask有两种操作模式:

(1)调用线程等待synctask完成;

(2)调用线程调度synctask,使之继续运行;

Synctask能够确保一个函数调用完成后,其对应的回调函数也会被执行到。

4.2.2.      synctask生命周期

一个synctask的完整生命周期包括如下几个阶段:

  • CREATED

当调用synctask_create或synctask_new函数创建一个synctask时;

  • RUNNABLE

当把synctask加入到syncenv的runq队列时;

  • RUNNING

当syncenv的工作线程调用synctask_switch_to函数时;

  • WAITING

当调用synctask_yield函数将一个synctask加入到syncenv的waitq队列时;

  • DONE

当一个synctask完成运行时。

以上几个阶段的状态转换如下图14所示:

图14 synctask状态转换

需要注意的是,在synctask的生命周期内,并不能保证该任务是一直在同一个线程中运行,每次调用synctask_yield函数时,可能会使其运行于一个不同的线程中。

总之,可以这样简单地理解,使用syncop框架执行一个任务时(例如syncop_opendir函数用于打开目录),调用函数会等待该任务完成后(目录已被打开),再继续往下进行。相对而言,syncop框架大大简化了数据均衡功能的实现。

 

4.3.    关键处理流程

每个节点的数据均衡进程,在处理流程上都是一样的,下面结合实际代码,分别简要分析一下数据均衡功能的主线程与迁移线程的处理流程。

主线程的入口函数是gf_defrag_start,该函数主要就是使用syncop框架创建一个synctask,用于本次数据均衡的处理工作。创建synctask同步任务时,指定了实际处理函数gf_defrag_start_crawl及其完成后的回调函数gf_defrag_done,下面首先给出从gf_defrag_start_crawl函数开始的一些主要处理流程,如图15、图16和图17所示。然后再列出迁移线程的主要处理流程,如图18所示。

4.3.1.      主线程处理流程

 

图15 gf_defrag_start_crawl函数处理流程

1、调用syncop_lookup,查询根目录的相关信息,位置、元数据和父目录等信息;

2、调用syncop_setxattr,修复根目录的哈希分布;

3、根据命令类型判断,如果命令类型只是修复目录哈希分布,则跳过迁移文件相关函数,转到步骤9;否则,下一步;

4、调用dht_get_local_subvols_and_nodeuuids,其内部使用了同步函数syncop_getxattr,获取本节点参与的子卷和每个相关子卷中涉及到的节点的uuid信息,并且在每个节点上的相同子卷返回的节点uuid的顺序一致;

5、调用gf_defrag_total_file_size,其内部使用了syncop_statfs,获取本节点参与子卷的总共文件大小;

6、调用gf_defrag_total_file_cnt,其内部使用了syncop_statfs,获取本节点参与子卷的总共文件数量;

7、调用gf_thread_create,创建一个文件计数线程,用于在数据均衡过程中统计文件迁移数量,方便用户命令行查看;

8、调用gf_thread_create,根据节点cpu活动processor的数量,创建多个迁移线程,负责具体文件迁移工作,其入口函数为gf_defrag_task,后面会给出该函数处理流程(图18);

9、根据命令类型判断,如果命令类型是开启分层(tier),则调用tier相关处理函数,由于本文不考虑tier相关操作,在此略过;否则,下一步;

10、调用gf_defrag_fix_layout,开始进行目录的修复与文件的爬取,这也是主线程的主要工作,下面会单独给出该函数的处理流程(图16);

11、在修复目录和爬取文件完成后,调用pthread_join,等待所有迁移线程完成;

12、所有迁移线程已经完成并返回,做些清理工作,并调用gf_defrag_done,结束均衡进程。

 

根据以上流程的说明,并结合前面介绍的工作机制等内容,要理解下面给出的gf_defrag_fix_layout和gf_defrag_process_dir函数处理流程并不会很难,具体过程不再赘述。

 

图16 gf_defrag_fix_layout函数处理流程

图17 gf_defrag_process_dir函数处理流程

4.3.2.      迁移线程处理流程

图18 gf_defrag_task函数处理流程

多个迁移线程同时运行,每个线程的处理流程都由gf_defrag_task函数负责,简要说明如下:

1、获取待迁移文件的队列defrag->queue;

2、进入如下while循环模式;

3、判断队列是否为空,如果不空,下一步;否则,转到步骤7;

4、调用list_del_init,从队列头取出一个文件,并更新相关计数;

5、调用gf_defrag_migrate_single_file,该函数负责单个文件迁移的整个过程,包括判断是否需要迁移、数据内容迁移、各种属性迁移、特殊文件处理和迁移后的清理工作等内容,此部分内容需要读者自行分析,不再赘述;

6、gf_defrag_migrate_single_file函数完成后,返回到步骤3;

7、判断文件爬取任务是否完成,如果没完成,则调用pthread_cond_wait,等待新文件迁移任务到来的通知;否则,下一步;

8、迁移任务都已完成,线程返回。

 

5. 问题与优化

尽管数据均衡功能经过了不断的完善,已经具有了良好的性能和扩展性,同时大大加快了扩容或缩容后集群数据均衡的进度,但仍然存在一些限制和问题,具体说明如下:

(1)数据迁移速度可进一步提升

原因是在迁移数据的时候,工作线程在读取原始文件和写入目标文件是串行的,如果能够将两者有效的分开独立进行,则可以进一步提升迁移速度;

(2)扩容或缩容时的数据迁移量大

在增加brick或删除brick的时候,需要对整个卷做数据均衡,这样一来,增加或删除很少brick时,也会触动整个集群均衡一次,给人一种牵一发而动全身的感觉,并且需要迁移的数据量较大,而在OpenStack Swift的数据均衡中则采用了带有虚拟节点的一致性哈希,可以一定程度减少数据的迁移量,因此可以考虑借鉴这种做法。

(3)数据迁移速度控制能力较弱

虽然目前命令行有三种模式(lazy、normal和aggressive),用于控制迁移速度,但还是属于比较粗略的控制,当有大量文件迁移时,还是会占用较多的系统资源,影响到存储系统性能。

(4)代码存在bug

例如,在GlusterFS的3.12系列之前的版本中,对分布式EC卷做扩容后做数据迁移的时候,每组EC卷只有一个节点在迁移文件,组内其他节点并没有参与该过程,这是和其预期工作机制是不相符的,并且影响了一部分文件的迁移。

 

上面只是列出了数据均衡目前存在一些限制,可能还有其他问题,等待读者去发现和解决。总之,未来还是有很多工作要做的,结合GlusterFS社区的讨论,列出主要内容如下:

(1)优化迁移速度,迁移数据的时候,读文件和写文件并行化;

(2)更精细地控制数据迁移,根据实际需要灵活地调控迁移速度;

(3)优化目录哈希分布,减少每次扩容或缩容后需要迁移的数据量;

(4)支持迁移暂停功能,再次开启后可以继续从当前暂停位置开始迁移;

(5)在数据均衡进程中增加性能模块(xlator),提高迁移效率;

(6)修复数据均衡代码中的bug。

6. 总结与展望

本文介绍了GlusterFS当前的数据均衡功能,内容包括数据均衡的产生背景、使用场景、基本原理、程序实现剖析、操作命令实践、存在的问题以及需要优化的地方等,同时结合实例说明了数据均衡的工作机制。

GlusterFS数据均衡功能还在不断的完善中,相信未来的数据均衡功能一定会更加高效和可控,并且占用较少的系统资源。

由于时间和精力有限,文中省略了很多细节,也包括一些重要的算法和流程,期望读者能够给予补充,同时理解不正确的地方也在所难免,欢迎指正。

(文章来自: 大魏分享 郭忠秋)

参考资料

[1] https://docs.gluster.org/

[2] http://pl.atyp.us/hekafs.org/index.php/2012/03/glusterfs-algorithms-distribution/

[3] http://staged-gluster-docs.readthedocs.io/en/release3.7.0beta1/Features/rebalance/

[4] https://github.com/gluster/glusterfs-specs/blob/master/done/GlusterFS%203.7/Improve%20Rebalance%20Performance.md

[5] glusterfs v3.12.9 source code

Creating Distributed Replicated Volumes

https://docs.gluster.org/en/latest/Administrator%20Guide/Setting%20Up%20Volumes/

Creating Distributed Replicated Volumes

Distributes files across replicated bricks in the volume. You can use distributed replicated volumes in environments where the requirement is to scale storage and high-reliability is critical. Distributed replicated volumes also offer improved read performance in most environments.

Note: The number of bricks should be a multiple of the replica count for a distributed replicated volume. Also, the order in which bricks are specified has a great effect on data protection. Each replica_count consecutive bricks in the list you give will form a replica set, with all replica sets combined into a volume-wide distribute set. To make sure that replica-set members are not placed on the same node, list the first brick on every server, then the second brick on every server in the same order, and so on.

砖的数量应该是分布式复制卷的副本计数的倍数。 此外,指定砖块的顺序对数据保护有很大影响。 您提供的列表中的每个replica_count连续砖将形成一个副本集,所有副本集合并为一个卷范围的分发集。 要确保副本集成员未放置在同一节点上,请列出每个服务器上的第一个块,然后列出每个服务器上相同顺序的第二个块,依此类推。

distributed_replicated_volume

To create a distributed replicated volume

  1. Create a trusted storage pool.
  2. Create the distributed replicated volume:

    # gluster volume create [replica ] [transport tcp | rdma | tcp,rdma]

    For example, a four node distributed (replicated) volume with a two-way mirror:

    # gluster volume create test-volume replica 2 transport tcp server1:/exp1 server2:/exp2 server3:/exp3 server4:/exp4
    Creation of test-volume has been successful
    Please start the volume to access data.
    

    For example, to create a six node distributed (replicated) volume with a two-way mirror:

    # gluster volume create test-volume replica 2 transport tcp server1:/exp1 server2:/exp2 server3:/exp3 server4:/exp4 server5:/exp5 server6:/exp6
    Creation of test-volume has been successful
    Please start the volume to access data.
    

    If the transport type is not specified, tcp is used as the default. You can also set additional options if required, such as auth.allow or auth.reject.

    Note: – Make sure you start your volumes before you try to mount them or else client operations after the mount will hang.

    • GlusterFS will fail to create a distribute replicate volume if more than one brick of a replica set is present on the same peer. For eg. for a four node distribute (replicated) volume where more than one brick of a replica set is present on the same peer.
      # gluster volume create <volname> replica 2 server1:/brick1 server1:/brick2 server2:/brick3 server4:/brick4
      volume create: <volname>: failed: Multiple bricks of a replicate volume are present on the same server. This setup is not optimal. Use 'force' at the end of the command if you want to override this behavior.
      

    Use the force option at the end of command if you want to create the volume in this case.

Creating Distributed Replicated Volumes

https://docs.gluster.org/en/latest/Administrator%20Guide/Setting%20Up%20Volumes/

Creating Distributed Replicated Volumes

Distributes files across replicated bricks in the volume. You can use distributed replicated volumes in environments where the requirement is to scale storage and high-reliability is critical. Distributed replicated volumes also offer improved read performance in most environments.

Note: The number of bricks should be a multiple of the replica count for a distributed replicated volume. Also, the order in which bricks are specified has a great effect on data protection. Each replica_count consecutive bricks in the list you give will form a replica set, with all replica sets combined into a volume-wide distribute set. To make sure that replica-set members are not placed on the same node, list the first brick on every server, then the second brick on every server in the same order, and so on.

砖的数量应该是分布式复制卷的副本计数的倍数。 此外,指定砖块的顺序对数据保护有很大影响。 您提供的列表中的每个replica_count连续砖将形成一个副本集,所有副本集合并为一个卷范围的分发集。 要确保副本集成员未放置在同一节点上,请列出每个服务器上的第一个块,然后列出每个服务器上相同顺序的第二个块,依此类推。

如何查看macbook 的配置详情比如cpu的具体型号

在笔记本要查看具体的CPU型号的,那么该如何详细查看的呢?如何可以知道详细的信息?

如何查看macbook 的配置详情比如cpu的具体型号

方法/步骤

  1. 笔记本点击启动台,点击其他。

    如何查看macbook 的配置详情比如cpu的具体型号
    如何查看macbook 的配置详情比如cpu的具体型号
  2. 笔记本点击终端,输入查看命令:【sysctl machdep.cpu.brand_string】。

    如何查看macbook 的配置详情比如cpu的具体型号
    如何查看macbook 的配置详情比如cpu的具体型号
  3. 回车键,即可以看到CPU型号。

    如何查看macbook 的配置详情比如cpu的具体型号
  4. 查看CPU多少核,那么输入命令:【sysctl -n machdep.cpu.core_count】,回车就看到双核。

    如何查看macbook 的配置详情比如cpu的具体型号
  5. 查看CPU线程,那什么输入命令:【sysctl -n machdep.cpu.thread_count】,回车键即可看到四线程。

    如何查看macbook 的配置详情比如cpu的具体型号
    如何查看macbook 的配置详情比如cpu的具体型号

通过iptables实现端口转发和内网共享上网

1. netfilter

iptables操作的是2.4以上内核的netfilter,所以需要 linux的内核在2.4以上。其功能与安全性远远比其前辈 ipfwadm, ipchains强大,iptables大致是工作在OSI七层的二、三、四层,其前辈ipchains不能单独实现对tcp/udp port以及对mac地址的的定义与操作,所以我想ipchains应该是仅仅工作在三层的。

我们先简单介绍一下netfilter的大致工作流程,也就是一个数据包(或者叫分组、packet,我个人习惯叫包)在到达linux的网络接口的时候 (网卡)如何处理这个包,然后再介绍一下如何用iptables改变或者说控制对这个数据包进行操作。

netfilter内部分为三个表,分别是 filter,nat,mangle,每个表又有不同的操作链(Chains)。

  • filter表:filter表用于防火墙功能,定义了三个 Chain,分别是INPUT(入), FORWARD(转发), OUTPUT(出)。
  • nat(Network Address Translation)表:nat表用以实现地址转换和端口转发功能,定义了三个Chain,分别是PREROUTING,POSTROUTING,OUTPUT。
  • mangle表:mangle表是一个自定义表,包括filter表、nat表中的各种chains,它可以让我们进行一些自定义的操作,同时这个mangle表中的chains在netfilter对包的处理流程中处在一个比较优先的位置。

下面有一张图清晰的描绘了netfilter对包的处理流程(该图摘自网上,不知作者是谁,在此深表敬意!),一般情况下,我们用不到这个mangle表,在这里我们就不做介绍了。

netfilter包处理流程

1.1 PREROUTING链

当一个包来到Linux的网络接口的时候先执行PREROUTING操作,依次经过mangle、nat的PREROUTING链。从这个Chain的名字我们可以看出,这个Chain是在路由之前(pre-routing)要过的。为什么要在路由之前过呢?因为在这个链里面我们对包的操作是DNAT,也就是改变目的地址和(或端口),通常用在端口转发(修改P
ort),或者NAT到内网的DMZ区(修改地址)。

环境配置如下:

//节点1 - 公网节点
eth0: 60.1.1.1/24 公网ip
eth1:10.1.1.1/24 内网ip

//节点2 - Web服务器
eth1: 10.1.1.2/24

我们怎么样能让Internet用户通过公网IP访问内部的web服务器呢? 在这个PREROUTING链上定义一个规则,把访问60.1.1.1:80的用户的目的地址改变一下,改变为10.1.1.2:80,这样就实现了internet用户对内网服务器的访问了。当然,这个端口是比较灵活的,我们可以定义任何一个端口的转发,不一定是80–>80。具 体的命令我们在下面的例子中介绍,这里我们只谈流程与概念上的实现方法。

1.2 FORWARD链

好了,我们接着往下走,来到图中下方的那个菱形(FORWARD),转发!

  • 如果目的IP是本机IP,那么包向上走,走入INPUT链处理,然后进入LOCAL PROCESS,随后执行OUTPUT。我们在这里就不介绍INPUT,OUTPUT的处理了,因为那主要是对于本机安全的一 种处理,我们这里主要说对转发的过滤和NAT的实现。
  • 如果非本地,那么就进入FORWARD链进行过滤。

默认情况下,当Linux收到了一个目的IP地址不是本地IP的包,Linux会把这个包丢弃。因为默认情况下,Linux的三层包转发功能是关闭的,如果要让我们的Linux实现转发,则需要打开这个转发功能,可以 改变它的一个系统参数,使用如下命令打开转发功能:

# 方法一
sysctl net.ipv4.ip_forward=1
# 方法二
echo "1" > /proc/sys/net/ipv4/ip_forward

处理顺序上,依然是mangle优先、随后流经filter的FORWOARD链。我们操作任何一个链都会影响到这个包的命运,在 下面的介绍中,我们就忽略掉mangle表,我们基本用不到操作它,所以我们假设它是透明的。假设这个包被我们的规则放过去了,也就是ACCEPT了,它将进入POSTROUTING部分。

注意!这里我注意到一个细节问题,也就是上面的图中数据包过了FORWARD链之后直接进入了POSTROUITNG 链,我觉得这中间缺少一个环节,也就是ROUTING。对于转发的包来说,Linux同样需要在选路(路由)之后才能将它送出,这个图却没有标明这一点,我认为它是在过了ROUTING之后才进入的POSTROUITNG。当然了,这对于我们讨论iptables的过滤转发来说不是很重要,只是我觉得流程上有这个问题,还是要说明 一下。

1.3 POSTROUITNG链

POSTROUTING链是数据包要送出这台Linux的最后一个环节了,也是极其重要的一个环节。这个时候Linux已经完成了对这个包的路由(选路工作),已经找到了合适的接口送出这个包了,在这个链里面我们要进行重要的操作,就是被Linux称为 SNAT的一个动作,修改源IP地址!

为什么修改源IP地址?最常见的就是我们内网多台机器需要共享一个或几个公网IP访问 Internet。因为我们的内网地址是私有的,假如就让Linux给路由出去,源地址也不变,这个包能访问到目的地,但却回不来。因为 Internet上的路由节点不会转发私有地址的数据包,也就是说,不用合法IP,我们的数据包有去无回。

有人会说:“既然是这样,我就不用私有IP了,我自己分配自己合法的地址不行吗?那样包就会回来了吧?”。答案是否定的,IP地址是ICANN来分配的,Internet上的路由器会把这个返回包送到合法的IP去,你同样收不到。而你这种行为有可能被定义为一种ip欺骗,很多设备会把这样的包在接入端就给滤掉了。

那么Linux如何做SNAT 呢?环境配置如下:

//节点1 - 公网节点
eth0: 60.1.1.1/24 公网ip
eth1:10.1.1.1/24 内网ip

//节点2
eth1: 10.1.1.2/24

当内网节点10.1.1.12需要访问202.2.2.2的web服务器,发送数据包时先路由到10.1.1.1节点,随后(在10.1.1.1节点配置SNAT)将源IP改为60.1.1.1后送出。同时在ip_conntrack表里面做一个记录:内网的哪一个ip的哪个端口访问的这个web服务器,自己把它的源地址改成多少了,端口改成多少了,以便这个web服务器返回数据包的时候linux将它准确的送回给发送请求的这个pc.

2. iptables命令

大体的数据转发流程我们说完了,我们看看iptables使用什么样的参数来完成这些操作。在描述这些具体的操作之前,我还要说几个我对iptables的概念的理解(未必完全正确),这将有助于大家理解这些规则,以实现更精确的控制。

上文中我们提到过,对包的控制是由我们在不同的Chain(链)上面添加不同的规则来实现的。那么既然叫链,一定就是一条或者多条规则组成的了,这时就有一个问题了,如果多个规则对同一种包进行了定义,会发生什么事情呢?

在Chain中,所有的规则都是从上向下来执行的,也就是说,如果匹配了第一行,那么就按照第一行的规则执行,一行一行的往下找,直到找到 符合这个类型的包的规则为止。如果找了一遍没有找到符合这个包的规则怎么办呢?iptables里面有一个概念,就是Policy(策略),如果找了一遍找不到符合处理这个包的规则,就按照policy来办。iptables 使用-P来设置Chain的策略。

2.1 链(Chain)操作

对链的操作就那么几种:

  • I(插入):放到链首
  • A(追加):放到链尾
  • R(替换)
  • D(删除)
  • L(列表显示)

比如我们要添加一个规则到filter表的FORWARD链:

# 追加一个规则至filter表中的FORWARD链尾,允许(-j ACCEPT)源地址为10.1.1.11目的地址为202.1.1.1的数据包通过。
iptables -t filter -A FORWARD -s 10.1.1.11 -d 202.1.1.1 -j ACCEPT

在iptables中,默认的表名就是filter,所以这里可以省略-t filter直接写成:

iptables -A FORWARD -s 10.1.1.11 -d 202.1.1.1 -j ACCEPT

2.2 参数项

iptables中的匹配参数: 我们在这里就介绍几种常用的参数,详细地用法可以man iptables看它的联机文档,你会有意外的收获。

  • -s 匹配源地址
    可以指定某一个单播ip地址,也可以指定一个网络.
    如果单个的ip地址其实隐含了一个32位的子网掩码,比如-s 10.1.1.11 其实就是-s 10.1.1.11/32,
    指定不同的掩码用以实现源网络地址的规则,比如一个C类地址我们可以用-s 10.1.1.0/24来指定。
  • -d 匹配目的地址
    规则和-s相同
  • -p 协议匹配
    如tcp, udp, udplite, icmp, esp, ah, sctp
  • -i 入接口匹配
    指定网络接口,比如我仅仅允许从eth3接口过来的包通过FORWARD链,就可以这样指定iptables -A FORWARD -i eth3 -j ACCEPT
    -o 出接口匹配
    规则同-i
  • –sport 源端口
  • –dport 源端口
  • -j 跳转
    也就是包的方向,包括ACCEPT, DROP, QUEUE or RETURN几种。

3. 操作示例

3.1 实例一:内网节点访问外网服务

环境信息:

#服务器节点
> SUSE Linux Enterprise Server 11 (x86_64)
eth0: 8.1.234.73(外网IP)
服务端口: 80

#外网节点
> SUSE Linux Enterprise Server 11 (x86_64)
eth0: 192.168.234.71(内网IP)
eth11: 8.1.234.71(外网IP)

#内网节点
> SUSE Linux Enterprise Server 11 (x86_64)
eth0:192.168.234.72(内网IP)

3.1.1 内网节点配置

  • 将内网节点的网关指向外网节点的内部IP:192.168.234.71。
vi /etc/sysconfig/network/routes 
------
default 192.168.234.71 - -
  • 重启网络服务
service network restart
  • 查看路由是否生效
route
------
 Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
default         192.168.234.71  0.0.0.0         UG    0      0        0 eth0
...

3.1.2 外网节点配置

  • 打开linux的转发功能
 sysctl net.ipv4.ip_forward=1

或者使用如下命令

echo 1 > /proc/sys/net/ipv4/ip_forward 
  • 配置FORWARD
//
# 将FORWARD链的策略设置为DROP,这样做的目的是做到对内网ip的控制
# 你允许哪一个访问internet就可以增加一个规则,不在规则中的ip将无法访问internet.
iptables -t filter -P FORWARD DROP

# 允许任何地址 --> 任何地址的确认包和关联包通过。
# 一定要加这一条,否则你只允许Lan IP访问没有用,至于为什么,下面我们再详细说。
iptables -A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT

# 允许内网IP数据转发
iptables -A FORWARD -s 192.168.234.72 -j ACCEPT
  • 配置SNAT
    将来自192.168.234.0/24的地址转换为8.1.234.71。因为是让内网上网,对于代理服务器而言POSTROUTING(经过路由之后的包应该要把源地址改变为8.1.234.71,否则包无法返回)。
iptables -t nat -A POSTROUTING -s 192.168.234.72/24 -j SNAT --to 8.1.234.71

配置完成,在内网节点(192.168.234.72)发起的服务器节点(8.1.234.73)请求,会由外网节点修改源ip后转发出去。
在8.1.234.73上启动nginx服务,在192.168.234.72上通过curl访问效果如下:

curl 8.1.234.73
------
Hello, this is 8.1.234.73 server

可以将上述配置写到一个文件中,以便重复执行。

//
# 清除filter配置后重新配置
iptables -t filter -F
iptables -t filter -P FORWARD DROP
iptables -A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -A FORWARD -s 192.168.234.72 -j ACCEPT
# 清除nat配置后重新配置
iptables -t nat -F
iptables -t nat -A POSTROUTING -s 192.168.234.0/24 -j SNAT --to 8.1.234.71

除此之外,也可以精确控制他的访问地址,比如我就允许10.1.1.99访问3.3.3.3这个ip

iptables -A FORWARD -s 10.1.1.99 -d 3.3.3.3 -j ACCEPT

或者只允许他们访问80端口

iptables -A FORWARD -s 10.1.1.0/24 -p tcp --dport http -j ACCEPT

更多的控制可以自己灵活去做,或者查阅iptables的联机文档。

3.2 实例二:外网节点访问内网服务

环境信息:

* 外网节点
SUSE Linux Enterprise Server 11 (x86_64)
eth0: 8.1.234.73(外网IP)

*  外网节点(网关节点)
SUSE Linux Enterprise Server 11 (x86_64)
eth0: 192.168.234.71(内网IP)
eth11: 8.1.234.71(外网IP)

* 内网节点(服务节点)
SUSE Linux Enterprise Server 11 (x86_64)
eth0:192.168.234.72(内网IP)
服务端口: 80

3.2.1 内网节点配置

  • 开启nginx服务
/etc/nginx/nginx

确保服务监听的是ip是内网ip

    server {
        listen     192.168.234.72:80;
        server_name  192.168.234.72;
        ......
  • 将内网节点的网关指向外网节点的内部IP:192.168.234.71。
vi /etc/sysconfig/network/routes 
------
default 192.168.234.71 - -
  • 重启网络服务
# service network restart
  • 查看路由是否生效
# route
 Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
default         192.168.234.71  0.0.0.0         UG    0      0        0 eth0
...

3.2.2 外网节点(网关节点)配置

  • 打开linux的转发功能
sysctl net.ipv4.ip_forward=1

或者使用如下命令

echo 1 > /proc/sys/net/ipv4/ip_forward 
  • 配置FORWARD
# 将FORWARD链的策略设置为DROP,这样做的目的是做到对内网ip的控制
# 你允许哪一个访问internet就可以增加一个规则,不在规则中的ip将无法访问internet.
iptables -t filter -P FORWARD DROP

# 允许任何地址 --> 任何地址的确认包和关联包通过。
# 一定要加这一条,否则你只允许lan IP访问没有用,至于为什么,下面我们再详细说。
iptables -A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT

# 将目的地址为外网地址的数据包改为内网地址
iptables -t nat -A PREROUTING -d 8.1.234.71 -p tcp --dport 80 -j DNAT --to 192.168.234.72

# 路由前,数据包先经过PREROUTING处理,目的地址已改为内网地址(192.168.234.72)
# 配置转发规则
iptables -t filter -A FORWARD -d 192.168.234.72 -p tcp --dport 80 -j ACCEPT

同样,可以将这部分配置放到一个文件中:

iptables -t filter -F
iptables -t nat -F
iptables -t filter -P FORWARD DROP

iptables -t filter -A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -t nat -A PREROUTING -d 8.1.234.71 -p tcp --dport 80 -j DNAT --to 192.168.234.72
iptables -t filter -A FORWARD -d 192.168.234.72 -p tcp --dport 80 -j ACCEPT

OK,至此配置完成,我们可以尝试在外网节点(8.1.234.73)上,通过网关节点(8.1.234.71)访问内网(192.168.234.72)提供的服务(80端口)。

Hello, this is 72 server

3.2.3 另一种配置方式

在前面的配置中,内网节点将互通的外网节点做为网关,由网关节点修改目的地址,源地址保持不变。其实,还存在另外一种配置方式,同时修改源地址、目的地址。将内网地址中的默认路由配置删除

route del default gw 192.168.234.71

增加一个SNAT配置

iptables -t nat -A POSTROUTING -d 192.168.234.72 -p tcp --dport 80 -j SNAT --to 192.168.234.71

这条命令不太好懂?其实很简单,如果使用这条命令,那么你的web server不需要再设置默认网关,就能收到这个请求,只要他和linux的lan ip地址是能互访的(也就是说web server和Linux的Lan ip在一个广播域)。我们在根据上面的netfilter流程图来分析这个包到底被我们怎么样了:

  1. 一个请求8.1.234.73:1333访问8.1.234.71:80被linux收到了,进入PREROUTING;发现一个匹配规则
iptables -t nat -A PREROUTING -d 8.1.234.71 -p tcp --dport 80 -j DNAT --to 192.168.234.72

修改目的地址,于是这个包变成了8.1.234.73:1333–>192.168.234.72:80。

  1. 进入FORWARD链,也有一条规则允许通过
iptables -A FORWARD -d 192.168.234.72 -p tcp --dport 80 -j ACCEPT

进入route box选路,找到合适路径,此时这个包依旧是8.1.234.73:1333–>192.168.234.72:80。

  1. 随后进入POSTROUTING链;又发现一个符合的规则
iptables -t nat -A POSTROUTING -d 192.168.234.72 -p tcp --dport 80 -j SNAT --to 192.168.234.71

原来是一个SNAT,改你的源地址,于是这个包变成了192.168.234.71:xxxx(随机端口)–>192.168.234.72:80。

  1. 整个的两次变化的过程都会记录在linux的ip_conntrack中。
  2. 当web server收到这个包的时候,发现,原来是一个内网自己兄弟来的请求阿,又在一个广播域,不用找网关,把返回包直接扔给交换机了;
  3. linux在收到返回包之后,会根据他的ip_conntrack中的条目进行两次变换,返回真正的internet用户,于是完成这一次的访问。

看了上面的两个例子,不知道大家是否清楚了iptables的转发流程,希望对大家有所帮助。

4. 状态机制

下面来讲前面提到的ESTABLISHED,RELATED规则是怎么回事,到底有什么用处。

我们知道,网络的访问是双向的,也就是说一个Client与Server之间完成数据交换需要双方的发包与收包。在netfilter中,有几种状态,也就是New, Established,Related,Invalid。

当一个客户端,在本文例一中,内网的一台机器访问外网,我们设置了规则允许他出去,但是没有设置允许回来的规则啊,怎么完成访问呢?这就是netfilter的 状态机制 ,当一个Lan用户通过这个Linux访问外网的时候,它发送了一个请求包,这个包的状态是New(配置了内网IP的转发规则,放行)。当外网回包的时候他的状态就是Established,所以,Linux知道,哦,这个包是我的内网的一台机器发出去的应答包,他就放行了。

而外网试图对内发起一个新的连接的时候,他的状态是New,所以Linux压根不去理会它。这就是我们为什么要加这一句的原因。

还有那个Related,他是一个关联状态,什么会用到呢?sftp、ftp都会用到,因为他们的传输机制决定了,它不像http访问那样,Client_IP: port–>Server:80然后server:80–>Client_IP:port,ftp使用tcp21建立连接,使用20端口发送数据,其中又有两种方式,一种主动active mode,一种被动passive mode。主动模式下,client使用port命令告诉server我用哪一个端口接受数据,然后server主动发起对这个端口的请求。被动模式下,server使用port命令告诉客户端,它用那个端口监听,然后客户端发起对他的数据传输,所以这对于一个防火墙来说就是比较麻烦的事情,因为有可能会有New状态的数据包,但是它又是合理的请求,这个时候就用到这个Related状态了,他就是一种关联,在linux中,有个叫 ftp_conntrack的模块,它能识别port命令,然后对相应的端口进行放行。

5. 实用命令

对了,还有几个在实际中比较实用(也比较受用:-))的命令参数,写出来供大家参考

  • 有的时候使用iptables -L会比较慢,因为linux会尝试解析ip的域名,真是罗嗦,如果你的dns server比较不爽的话,iptables -L就会让你很不爽,加一个-n参数就好了。列表刷的就出来。当然了,如果你的linux就是做防火墙,建议把nameserver去掉,在 /etc/resolve.conf里面,因为有时候使用route命令也会比较慢列出来,很是不爽。
iptables -L -n
  • 这个命令会显示链中规则的包和流量计数,嘿嘿,看看哪些小子用的流量那么多,用tc限了他。
iptables -L -v
  • 查看nat表中的规则。
iptables -t nat -L -vn
  • 查看目前的conntrack,可能会比较多哦,最好加一个|grep “关键字”,看看你感兴趣的链接跟踪
cat /proc/net/ip_conntrack
  • 看看总链接有多少条。
wc -l /proc/net/ip_conntrack
  • 把当前的所有链备份一下,之所以放到/etc下面叫iptables,因为这样重起机器的时候会自动加载所有的链,经常地备份一下吧,否则如果链多,万一掉电重启,你还是会比较痛苦。
iptables-save >/etc/iptables

作者:随安居士
链接:https://www.jianshu.com/p/2312dd32361a
来源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。