学习了好多集群的概念和理论…要好好消化一下, 所以来个小插曲转移一下注意点啦~
SCSI和iSCSI协议
我们曾经在很远很远的时候说过简单的计算机硬件基础, 当时我们就已经说过了总线这东西, 离CPU较近的那一边就叫做北桥, 而较远的那就叫做南桥. 下面是一个简单的图示:
不过虽然是这样画的 现代的服务器的架构肯定是和这个有一些不同的, 不过大体上是一样的. CPU通过局部总线(Local Bus)连接上缓存和北桥控制器, 而控制器通过内存总线和内存进行连接, 中间的叫做PCI总线又接上了很多控制器, 例如SCSI控制器, USB控制器, 等等.
我们之前在说存储类型的时候说过, DAS, NAS, 和SAN. DAS就好比我们直接把移动硬盘接进来, 这就是DAS, 即直接附加存储(Direct Attach Storage). 而NAS设备就是使用一些文件共享协议实现的文件服务器, 好比NFS, CIFS等. 有些NAS也可以使用应用层的HTTP协议进行文件传输. (RESTful). 还有就是SAN, 存储区域网络(Storage Area Network) 这些设备通过的网络, 就是SAN. SAN能够块级别的文件服务, 其实SAN主要就是做到对特定的物理层的解码和编码, 例如FC SAN
就是为了光信号的文件传输的. 而IP层的就是可以提供IP层的协议报文的封装和解包.
为了搞清楚这些玩意, 我们就要来看看Linux(RHEL)的存储模型是个什么样的, 或者说调用接口是怎么组织的:
首先我们从上层来看, 有存储数据或者读取数据的需求的显然是我们的应用程序, 接着我们的Linux为各种类型的文件系统抽象出统一的接口, 即上面的VFS. 接着的File System Driver就是我们的各种类型文件系统的具体实现了, 接着驱动实际的有存储能力的磁盘就是向下一层的Block Device Driver. 最后的Volume就不需要多说了.
而为了要是需要操作基本的存储卷的话, 就需要将DAS接到总线上, 这些接口有这些: IDE, SCSI ( 这两个都是并行的 ), 串行的: SATA, SAS, USB, eSATA. 我们今天的主角就是SCSI了, SCSI的全称是小型计算机接口(Small Computer System Interface)
刚刚也说了, SCSI是一种并行的IO技术, 并且规范了相关的协议. 对于SCSI而言, 他的数据传输是以块的方式进行的. SCSI有这些特点:
- 设备无关性
- 多设备并行
- 高带宽 ( 和IDE相比较 )
- 低系统开销 全能总线, 可以自行执行很多操作, 几乎不需要CPU的参与, 是IDE的1/10
接下来我们来说说SCSI控制器的工作模式:
首先, 在逻辑上, 我们把SCSI控制器分为一个任务管理单元和多个逻辑管理单元. 对于每一个逻辑设备, 我们也为他们分配一个ID号码来辨识, 叫做LUN.
SCSI的通信模型是一个C/S架构的模型, 也就是我们常说的客户端,服务器端架构, 我们把客户端叫做Initiator, 而另外一端, 即Target端, 他们之间通过SCSI通道连接互相的SCSI port进行通信. 更有意思的是, 理解SCSI十分容易, 如果你对网络分层的理解不错的话, 因为他们之间的分层架构和我们的网络十分相像:
相信一看就能明白, 然而我想说的一点是, 现在的技术已经可以将物理层替换成其他的传输介质, 不一定非要是SCSI线缆. 也可以是FC光缆, iSCSI, IB什么的, 这样, 通过各种不同的组织方式, 我们就可以构建承载着存储协议SCSI的相关报文的网络, 这就是一个SAN了.
每一个SCSI硬盘, 他的内部都有一个或者数个由逻辑单元,任务管理器,服务器构成的组件, 和一个任务分发器.
为什么说这个呢? 我们来思考一个问题, 如果说不谈物理层面上那个具有存储能力的磁盘, 如果说我随便找一个磁盘, 但是我把它接到一个Linux主机上, 该主机实现了上面 我说的那些功能, 那么我说这个是一个SCSI盘也是没有什么毛病的.
换言之, 我们通过软件是完全可以把一个IDE硬盘驱动成SCSI的. 这个其实就是iSCSI.
来看下面的一个图:
这是iSCSI和SCSI/FC的对比. SCSI/FC很简单了, 只要有FC或者SCSI驱动,光卡和专用线缆就可以了, 主要是看看我们的iSCSI.
首先, 为了封装得到SCSI报文, 我们需要SCSI驱动, 接着由iSCSI驱动承上启下转换成为TCP/IP报文, 接着通过网卡传输到存储端, 而当然了, 存储端同样也是要求拥有网卡的. 也就是说我们要在原始的数据前面加上SCSI首部, iSCSI首部, TCP首部, IP首部, Ethernet首部. 这样多层封装的结果就是, 性能不会这么好.
接下来我们就来看看SAN和NAS的对比吧:
主要的区别我们之前也都说过了, 向外输出的接口就是很不一样的, 一个是块级别的, 一个是文件级别.
最后 iSCSI协议监听在tcp的3260端口上.
Linux IP SAN的实现
现在我就先来实现一下Target端, 并且在上面配置LUN. 接着我们在找一台主机当做Initiator端, 看他能不能发现网络上的SCSI设备并且格式化并使用, 以及当出现了另外一个Initiator时, 同时读写同一个文件会造成什么情况.
首先, 对于网络上的资源, 显然加密是必然的, 尤其是对于这样的共享存储, 一定是需要进行认证的. 我们的认证方式有两种, 一个是基于IP的, 一个是基于CHAP的.
我们的CentOS自带的程序包叫做scsi-target-util
. 所提供的管理工具叫tgtadm, 他的配置结果保存在内核中, 一旦重启内核即失效. 另外一个叫做tgt-admin: 是通过读取配置文件来进行配置.
启动服务之后, 模拟的实际上是SCSI总线, 所以我们可以管理多个target, 而每个target也可以管理多个lun.
对于Initiator端, 我们也需要一个程序包iscsi-initiator-utils
. 他有两个工具: iscsi和iscsid. 前者是为了建立连接, 后者是为了进程随时能够读取iSCSI服务.
那么怎么识别呢? 其实, 对于target和Initiator端都使用了一个叫做iqn的来进行标识. 即 iSCSI qualified name. 是这样的格式:
iqn.YEAR-MONTH.tld.domain:string[.substring]
例如: iqn.2017-10.com.baidu:i1.c1
接下来我就开始配置iSCSI server了.
大体的几个步骤下面列出的这几个:
- 准备磁盘设备, 这个我们可以直接使用Vmware的虚拟磁盘来代替
- 安装程序包, 启动服务
- 创建target
- 创建lun
- 授权
OK, 现在我们准备三台主机, 两台作为Initiator端, 一台作为Target端.
Target端的配置
在Initiator端, 我准备了两块磁盘每个大小是10G的.
1 | [root@VM-node2 ~]# cat /dev/sd |
这两块磁盘不需要进行分区, 也不需要进行格式化. 接着就安装软件吧, 我们来看看生成了哪些东西:
1 | [root@VM-node2 ~]# rpm -ql scsi-target-utils |
反正这个守护进程无非就是在监听3260/tcp端口, 所以我们可以直接启动它:
1 | [root@VM-node2 ~]# systemctl start tgtd |
我们先来试试tgtadm这个命令行工具, 熟悉一下具体的工作流程, 接着再试试tgt-admin通过配置配置文件来简化流程.
1 | [root@VM-node2 ~]# tgtadm --help |
基本上我们的tgtadm是一个模式化的工具, 一共提供了三个模式:
target, 用来管理target的; logicalunit就是用来管理lun的; account是用来进行用户账户管理的.
每个模式又有着对应的操作, show, new, delete, update, bind(授权), unbind(解除授权)
现在就来进行创建一个target试试:
1 | [root@VM-node2 ~]# tgtadm --lld iscsi --mode target --op show |
上面显示了当前target的信息, 可以看到, 默认已经有一个LUN了, 他的类型是控制器.
所以再添加LUN的时候就不能再使用这个0号了. 现在来添加一个:
1 | [root@VM-node2 ~]# tgtadm --lld iscsi --mode logicalunit --op new --tid 1 --lun 1 --backing-store /dev/sdb |
这个时候就能看到我们添加的那个LUN了, 1号. 在添加的时候要使用--backing-store
指明使用什么设备, 只要是块设备就行了, 也就是说LVM和RAID都是可以的.
删除操作也是很容易理解的, 现在我们把另外一个sdc也加进来.
1 | [root@VM-node2 ~]# tgtadm --lld iscsi --mode logicalunit --op new --tid 1 --lun 2 --backing-store /dev/sdc |
接下来做什么? 授权. 其实对IP授权是比较多见的, 我们先来看一下:
1 | [root@VM-node2 ~]# tgtadm --lld iscsi --mode target --op bind --tid 1 --initiator-address 192.168.206.0/24 |
这个时候ACL信息就有了.
其实刚才的这些选项都是有短格式的, 例如:
- –lld, -L
- –mode, -m
- –op, -o
- –tid, -t
- –lun, -l
- –backing-store, -b
- –initiator-address, -i
- –targetname, -T
这样我们的target就已经配置完了, 很easy吧. 接下来就是另外一边的initiator端了.
Initiator端的配置
显然我们还是要先安装软件程序包, 配置Initiator的名字, 并且启动服务. 接着再使用iscsiadm实现对Target端的发现和注册等.
1 | [root@VM-node1 ~]# rpm -ql iscsi-initiator-utils |
这个玩意安装的东西有点多了吧, 提供了多个程序. 真正的管理工具就是那个iscsiadm. 有意思的是, 他有两个服务, 一个是iscsi, 一个是iscsid. 前者是为了实现在开机之后自动连入上一次连接的target, 而iscsid是为了实现数据的持久传输, 所以这些服务我们都需要启动.
iscsiadm也是一个模式化的工具, 一般我们使用的模式也就只有discovery和node模式, 偶尔可能会需要进行discoverydb的管理, 那就是discoverydb模式了.
首先查看一下/etc/iscsi/initiatorname.iscsi
这个文件, 它里面是我们这个Initiator的名字, 不过这个显然不适合我们. 所以我们需要重新修改一下:
1 | [root@VM-node1 ~]# cat /etc/iscsi/initiatorname.iscsi |
对于后面那个随机的字串, 我们可以使用iscsi-iname
这个工具生成, 用法很简单, 只要使用-p
指明前缀就行了:
1 | [root@VM-node1 ~]# echo "InitiatorName=$(iscsi-iname -p iqn.2017-10.com.justin)" > /etc/iscsi/initiatorname.iscsi |
现在就可以启动服务并且尝试进行发现了.
1 | [root@VM-node1 ~]# systemctl start iscsi |
来稍微解释一下, 这里的参数其实不止这些, 首选-d参数指明debug等级, 还有一个-P参数指定显示信息的等级, 接着-t指明类型, 这里我们使用到的是sendtargets(st)类型, 接着后面是使用-p参数指明IP和端口, 端口可省.
从输出信息中也可以看到, 已经发现了我们的s1, t1. 接着, 如果发现了target, 服务就会在/var/lib/iscsi/send_targets
创建出来一个对应的IP和端口的目录文件, 里面会存在我们刚刚发现的target. 当然 这个时候我们还不会出现映射过来的磁盘.
1 | [root@VM-node1 ~]# ls /var/lib/iscsi/send_targets/192.168.206.10,3260/ |
因为我们还没有登录进来, 所以这个时候就需要进入node模式了.
node的选项挺多, 但最后我们可以简化一下:
1 | [root@VM-node1 ~]# iscsiadm -m node -d 1 -T iqn.2017-10.com.justin13wyx:s1.t1 -p 192.168.206.10:3260 -l |
其中, -T表示对应的target, -p指明IP地址和端口, 端口可省, 最后的-l表示进行登录(log), 除了登录, 还有-u登出(logout).
试一下吧:
1 | [root@VM-node1 ~]# iscsiadm -m node -d 1 -T iqn.2017-10.com.justin13wyx:s1.t1 -p 192.168.206.10:3260 -u |
当我们登录的时候, 我们就会建立一条持久连接. 而且, 即使我们现在登出了, 下一次还是会自动的连接过来. 如果不想自动连接过来, 还记得我们之前在/var/lib/iscsi/send_targets
看到的那个目录下的软连接吗? 我们可以通过移除那个来搞定, 可以通过命令:
1 | [root@VM-node1 ~]# iscsiadm -m node -d 1 -T iqn.2017-10.com.justin13wyx:s1.t1 -p 192.168.206.10:3260 -o delete |
这个时候就没有了.
那我们现在再次发现和登录一次, 接着进行分区和格式化并且挂载使用一下试试看:
1 | [root@VM-node1 ~]# iscsiadm -m discovery -t st -p 192.168.206.10 |
接着挂载上来:
1 | [root@VM-node1 ~]# mkdir /data/iscsi -pv |
我们向里面写一个issue文件好了:
1 | [root@VM-node1 iscsi]# echo "RedHat CentOS7 (192.168.206.9)" > issue |
接着我们开启另外一台主机, 同样也试试挂载使用:
1 | [root@VM-node3 ~]# echo "InitiatorName=$(iscsi-iname -p iqn.2017-10.com.justin13wyx)" > /etc/iscsi/initiatorname.iscsi |
没有任何问题, 所以说尽管我们知道不能进行两次挂载, 但是我们却仍然可以进行挂载!
接下来我们玩一个好玩的, node1对issue进行写入, 我们来看看node3的状态:
1 | [root@VM-node3 iscsi]# cat issue |
果然是没有改变的, 因为我们说过这种共享存储都是在内存中进行的操作, 接下来我们把node1上的那个卸载掉:
1 | [root@VM-node1 iscsi]# cd ~ |
还是没有改变吧, 好吧, 另外一边也卸载掉.
1 | [root@VM-node3 iscsi]# echo "node3" > issue |
接着node1重新恢复:
1 | [root@VM-node1 ~]# mount /dev/sdb1 /data/iscsi/ |
看, 显示的node3最后修改的结果, 也就是说在互相挂载的状态下, 数据是不一致的, 这样就会发生错乱. 所以, 我们应该在一个target上提供多个硬盘来进行分开使用, 避免出现这种数据紊乱的情况.
以上, 就是我们说的使用命令行的方式进行的配置. 接下来我们再来看一下使用配置文件的方式, 现在就很简单了, 不过我们要先把现在的target删除:
1 | [root@VM-node2 ~]# tgtadm -m logicalunit -o delete -t 1 -l 1 |
出现了错误提示, 因为我们还没有登出, 两台Initiator都执行了登出操作之后就可以删除了:
1 | [root@VM-node2 ~]# tgtadm -m target -o delete -t 1 |
现在我们直接来看配置文件: (/etc/tgt/targets.conf
)
1 | default-driver iscsi |
十分好懂吧.
接着直接重启服务就行了:
1 | [root@VM-node2 ~]# tgtadm -m target -o show |
target的名字, LUN还有ACL授权都已经OK了.