0x01 概述

由于存储空间不大够,就打算组建一个nas存放数据和游戏。所以用闲鱼买的主机和机械排一排组建了一个内网的iSCSI服务器,然后把游戏全部存在了上面,减小笔记本硬盘的压力。

0x02 网络层面设计

在网络层面,由于机械硬盘的读写上限在100-200MB/s左右,所以2.5G网络和万兆网络意义不大。在网络层面选择了千兆交换机和千兆路由,保证基本上能跑满server和client的千兆口,千兆的下行宽带即可。

0x03 服务端配置

服务端配置分为存储层和iscsi层。由于机械硬盘随机读写性能较差,所以使用5g的ssd分区作为hdd的缓存。然后在上面建立ext4分区,再建立lvm,在逻辑卷上建立ntfs分区。这样可以快速调整分区大小,便于后续增加硬盘容量。iscsi server使用了tgt。由于只在内网使用,不使用iSCSI的身份验证功能(CHEP),所以服务端配置较为简洁。server软件选择tgtd,然后在/etc/tgt/targets.conf 中配置相应的存储路径:

<target iqn.2024-11.lvxy:lvxy>
 backing-store /dev/ubuntu-ext-store/iscsi_disk
</target>

iqn.2024-11.lvxy:lvxy 是initiator名称,一般按照iqn.日期.名称:名称,具体可以看华为的文章:修改不同主机上iscsi启动器名称一致的问题。/dev/ubuntu-ext-store/iscsi_disk是要共享的分区所在的位置。

在服务端配置完iscsi服务后,防火墙开放3260端口(iSCSI服务默认端口),内网中的机器即可扫描到iscsi服务器。

firewall开放端口:

firewall-cmd --zone=public --add-port=3260/tcp --permanent
firewall-cmd --reload

0x04 iSCSI client端配置

Windows端client使用系统自带的iSCSI Initiator。Initiator初次启动时会要求我们开启iSCSI服务,选择“是”即可,然后刷新一下列表,点击目标服务器ip即可自动连接。如果没有扫描到ip可以手动输入服务器ip地址,或者查看iSCSI服务器防火墙和服务配置。

Linux这边我使用ubuntu发行版,所以client端使用open-iscsi连接iscsi服务。

首先安装open-iscsi服务。

sudo apt install open-iscsi # 安装open-iscsi服务。接下来进行客户端iscsi初始化。
iscsiadm -V # 查看版本号,有输出版本号说明安装成功

然后初始化iscsi连接。一般直接执行下面的命令即可

sudo iscsiadm -m discovery -t st -p <server ip>:<server port> -l # 这条命令会直接登陆对应服务器上所有的目标

如果不想登陆所有的目标,上面的命令替换为:

sudo iscsiadm -m discovery -t st -p <server ip>:<server port> # 这里会给出对应server的ip,端口号,数量和iscsi initiator name
#比如127.0.0.1:3260,1 iqn.2024-11.lvxy:lvxy   后面iqn开头即为initiator name,复制想要登陆的名称
sudo iscsiadm -m node -T <initiator name> -l # 登陆对应目标

在登陆之后,即可使用fdisk或者系统自带的Partition manager, Disk等GUI分区查看软件查看到对应的分区,设备名称通常是Virtual Disk。然后使用mount挂载分区即可。

0x05常见问题处理

client端

windows端initiator处理了大多数异常情况,比如断网等,无法连接时会自动关闭session,断开iSCSI驱动器连接,所以目前暂未发现问题。

开机卡死

由于open-iscsi服务和iscsid服务默认超时时常为“no limit",开机时若没有网络或者无法连接到对应iscsi目标,systemd进程会卡在启动iscsi服务。我们修改对应 .service 文件即可。client端的iscsi有两个服务,一个是iscsid.service,一个是open-iscsi,分别对应iscsi的守护进程和登陆命令。两个service文件分别在:/usr/lib/systemd/system/iscsid.service ,/usr/lib/systemd/system/open-iscsi.service 。在[Service]部分添加TimeoutStartSecTimeoutStopSec即可,其他不用修改。

iscsid.service:

[Unit]
Description=iSCSI initiator daemon (iscsid)
Documentation=man:iscsid(8)
Wants=network-online.target remote-fs-pre.target
Before=remote-fs-pre.target
After=network.target network-online.target
DefaultDependencies=no
Conflicts=shutdown.target
Before=shutdown.target
ConditionVirtualization=!private-users

[Service]
Type=forking
PIDFile=/run/iscsid.pid
ExecStartPre=/usr/lib/open-iscsi/startup-checks.sh
ExecStart=/usr/sbin/iscsid
TimeoutStartSec=3s   #启动超时3s
TimeoutStopSec=10s   #关闭超时10s

[Install]
WantedBy=sysinit.target

open-iscsi.service:

[Unit]
Description=Login to default iSCSI targets
Documentation=man:iscsiadm(8) man:iscsid(8)
Wants=network-online.target remote-fs-pre.target
After=network-online.target iscsid.service
Before=remote-fs-pre.target
DefaultDependencies=no
Conflicts=shutdown.target
Before=shutdown.target
# Must have some pre-defined targets to login to
ConditionDirectoryNotEmpty=|/etc/iscsi/nodes
# or have a session to use via iscsid
ConditionDirectoryNotEmpty=|/sys/class/iscsi_session

[Service]
Type=oneshot
RemainAfterExit=true
# iscsiadm --login will return 21 if no nodes are configured,
# and 15 if a session is alread logged in (which we do not
# consider an error)
SuccessExitStatus=15 21
# Note: iscsid will be socket activated by iscsiadm
ExecStart=/usr/sbin/iscsiadm -m node --loginall=automatic
ExecStart=/usr/lib/open-iscsi/activate-storage.sh
ExecStop=/usr/lib/open-iscsi/umountiscsi.sh
ExecStop=/bin/sync
ExecStop=/usr/lib/open-iscsi/logout-all.sh
TimeoutStartSec=3s    #启动超时3s
TimeoutStopSec=10s    #关闭超时10s

[Install]
WantedBy=sysinit.target
Alias=iscsi.service

除了iscsi服务,mount也会导致卡死。若设置了fstab自动挂载,但是没有登陆到对应iscsi目标,也会使系统一直在等待对应iscsi驱动器。这个可以在fstab文件中iscsi驱动器对应行添加_netdev挂载参数,这样会告知相关服务这个驱动器要在网络连通后才有,可以避免开机卡死在等待对应设备。

比如:

UUID=20F49C29F49BFF6A            /media/lvxy/game_vol       ntfs    _netdev,defaults,uid=1000,gid=1000     0 0 
使用mount挂载之后普通用户无法读取

由于ntfs分区权限系统和linux权限系统不一样,linux挂载ntfs目录时会使用root挂载,导致普通用户没有访问权限;挂载ext分区不会有这个问题。

解决方案:挂载时在-o部分添加uid=<user uid>,gid=<user gid> 挂载参数;或按照上面的例子在fstab文件对应行添加uid=<user uid>,gid=<user gid> 。<user uid>和<user gid>为想读取分区的用户的uid和gid.

完整mount命令

o

0x06 下一步规划&结语

现在iscsi已经可以在linux上正常运行,但是不如Windows上的体验流畅,有些功能比如无法连接到对应iscsi目标时自动取消挂载,重新连接到目标时自动挂载等还不能实现。后续再研究一下iscsi的命令和服务实现这个功能,实在不行尝试写个脚本检测连接状态和挂载硬盘。

不过,总的来说,使用iscsi后体验还是不错的,可以在不添加连接线不加装硬盘(主要是我没有新的硬盘位了T_T)的情况下增加PC的存储容量。而且大部分游戏体验还可以,地平线4可以完美流畅运行(地平线优化真的很好),CS局中不卡顿(加载时间有一点长),黑猴也能勉强运行(有一点掉帧)。

至于为什么不选择SMB,则是因为部分游戏不支持网络存储,像原等,只支持本地驱动器,而iSCSI则解决了这个问题。注意,这里网络驱动器是指SMB、FTP这样的使用网络协议获取文件的驱动器,iscsi虽然使用了网络,但是宏观上看系统和驱动器、分区之间的数据交换还是使用了SCSI协议,和直接装在PC上的硬盘差不多,而且两台及其之间的通信完全可以由专用iscsi卡实现,OS层面不会感知到网络在中间的作用,因此应该算本地驱动器(Windows其实也是如此分类,在文件管理器中iscsi驱动器和本地驱动器是在一起的)。