前言:iSCSI 作为最通用的块存储协议,在数据中心、计算集群里获得了广泛应用。

iSCSI 协议的配置比 NFS、SMB/Samba 等常见的文件共享协议要复杂很多,其中涉及到 target、initiator、session 等多个概念,本文尝试从服务端和客户端两个角度进行配置。

  • 服务端
    • TrueNAS SCALE
    • Debian 12
  • 客户端
    • Windows 10/Server
    • Debian 12

1. iSCSI 服务端配置·

iSCSI 的配置非常复杂,介绍复杂系统一般有两种写作方法。第一种是先介绍前置知识,然后介绍技术流程,第二种是在技术流程中随机插入需要的前置知识。前者体系性较好,但是容易让读者在阅读前期理解困难。后者从流程出发,故事线漂亮,但是缺乏对知识的梳理。本文采取第一种介绍流程。因此为了保证准确理解 iSCSI 服务端配置过程中的一些术语,我要对下面几个词组进行解释。

  • Portals
    • 指的是存储区域网络(SAN)中的一个网络位置,由 IP 地址和 TCP 端口(一般是 3260)构成,可以把它想象成一扇门,后面是 initiator 可以连接到的存储对象。该目标用来进行 SAN 网络中的寻址。
  • Initiators 和 Target
    • iSCSI 发起者,即 iSCSI 协议中的客户端。在原版的 SCSI 协议中,initiator 发起 SCSI 命令的设备,通常是一台电脑或者一个主机控制器;Target 通常指的是接受或执行 SCSI 指令的设备,通常是硬盘驱动器或者磁带库。iSCSI 继承了 SCSI 的相关术语。Target 的概念在后面会详细介绍。
  • Initiator Name 与 iqn
    • 这个选项非常重要,它决定了在存储网络中这台设备的名字。因为服务一般需要有 DNS 支持,现代资本主义社会中,每个公司的内部数字资产都有一套严格的、规范的命名方式。比如我这台机器被命名为 epyc-truenas,所属的一级域名是 littlenewton.cn,我于 2019 年 8 月获得该域名。根据这些信息,按照 RFC3721 的要求,倒序打点连接起来便得到 initiator-name:iqn.2019-08.cn.littlenewton:epyc-truenas,其中 iqn 指的是 iSCSI Qualified Name. 这个名字会用来进行身份验证。
  • Initiator Groups (重点)
    • 又称 ACL,即一般意义上的访问控制列表。以 Initiator Group ID 为唯一标识(主键),服务器可以把某些 iSCSI 客户端的 iqn 添加到这个组里,实现访问控制。通过 Initiator Groups,服务器可以控制哪些 Initiator 有资格访问哪些 Targets.
  • Targets
    • 指的是在 Portal 后面真正提供存储的 iSCSI 对象,Initiator 通过 SAN 网络连接到 Target 即可获得存储服务。Target 本身也有自己的名字,该名字和存储服务器的 iqn 连接在一起,就构成了这个 Target 的 iqn,如 iqn.2019-08.cn.littlenewton:epyc-truenas:openwrt-code 指的是 littlenewton.cn 组织的 epyc-truenas 存储服务器上的一个名为 openwrt-code 的存储对象。
  • Extents
    • 这是 iSCSI 中最底层的概念,指的是 Target 统领的一段“存储空间”,它可以是某个物理磁盘,也可以是某个磁盘上的某个分区,也可以是某个 ZFS Pool 中的一个 ZVoL. 注意,TrueNAS SCALE 目前不支持把物理磁盘作为 Extent 使用,而 TrueNAS CORE 是可以的。一个 iSCSI Target 可以对应多个 Extents,这个观察非常关键。通过下面两张图片可以观察到确实如此。如果强行把 Target 理解成一个硬盘,那么 Extent 就是上面的分区,因此挂载一个 Target 就相当于把所有分区都挂载上。这么理解没有问题,但是没有用。

图 1. iSCSI 服务端的 Extents 列表,我们创建了两个用来演示的 Extent.

图 2. 在 Debian 12 客户端挂载 iSCSI 目标之后,查看系统里硬盘的详细信息。可以发现 wwn 号与 TrueNAS 服务端的 Extent NAA 号严格对应。

由此我们简单总结一下:

  • Portal 代表 SAN 网络中的网络位置,其实就是 IP 地址加 TCP 端口号。
  • Target 代表一个存储服务依赖的资源,这是一个抽象概念,它既不是一个硬盘,也不是一台服务器。在现实中找不到很一致的类比。
  • Extent 指的是一个逻辑硬盘(LUN),多个 Extents 可以挂在同一个 Target 上,这样通过一次 iscsiadm login 操作就能识别 SAN 中多个硬盘,这多个硬盘就构成了一个独立的服务。这与我们日常的操作逻辑是完全一致的。

可以通过下图理解一下 SAN 网络的拓扑结构。

图 3. 笔者实际生产环境中的 SAN 布局。

重点看一下一个 Target (cloud) 对应多个 Extents (nutstoreonedrive) 的情况。

图 4. 在 TrueNAS 上查看关联关系,可以发现一个 Target 后面可以挂多个 Extent LUN 实例。

1.1 TrueNAS SCALE·

1.1.1 配置 iqn·

TrueNAS SCALE 作为通用存储的开源解决方案,配置 iSCSI 共享非常简单。在 Shares 界面找到 Block (iSCSI) Shares Targets 选项卡,点击 Configure,首先修改 Base Name. 改成合适的 iqn.

图 5. 配置 TrueNAS 的 iqn 名字,可以更好地对内网资源进行统一命名、管理。这是个好习惯。

1.1.2 配置 Portals·

如前所述,Portal 就是一个网卡和监听端口。此处不再赘述。

图 6. Portal 就是个 IP 地址和端口号的组合。如果有多个网卡,一定要仔细设置 Portal 的细节,防止在不合适的网络 Interface 上做共享。

Portals 里面也可以配置 CHAP 加密,但是 TrueNAS SCALE 的设计有问题,Authorized Access 引用 ID 会产生重复。鉴于不加认证就能发起 Discovery 并不会造成很大风险,因此可以忽略这个认证。

图 7. Portal 的细节也是可以编辑的。

1.1.3 配置 Initiator Groups·

如前所述,Initiator Group 在术语介绍部分讲过,是用来做 ACL 的重要信息。Group ID 将被填写到某些 Target 里,iqn 存在于 Group 里的 Initiator 才能在 discover 阶段发现这些 Target.

图 8. Initiator 列表详情。通过 Initiator 做限制,防止不被授权的用户发现 Target.

1.1.4 配置 Authorized Access 密码本·

某些 Initiator 将会使用 CHAP 加密建立 iSCSI 通信,因此,从逻辑上讲需要在 iSCSI 服务器上维护一个用户、密钥列表。Authorized Access 就是做这个工作。

图 9. 为了做加密和访问控制,需要在服务端做一个授权模型,比如建立一个授权列表,里面就记录了合法用户的 iqn.

图中虽然存在一个 Group ID,但它只是用来标识这一行用户、密码。填写到 Target 里的也只是这个 ID,因此,原则上一个 iqn 可以拥有多个密码,但这是很不常见的。

1.1.5 配置 Targets·

上述基本都是为了配置 Target 而存在。

图 10. 通过服务类型,设置多个 Target,让某个服务连接到一个 Target 之后即可挂载后面的所有 LUN,防止重复操作。

让我们看一下细节:

图 11. Target 的细节设置里有很多内容,而且是与之前的设置紧密相关,所以这一部分最后设置比较好。

Target 作为连接 endpoint,在安全性上可谓是层层加码:

  1. 为了防止不同 SAN 网络的机器通过多网卡胡乱连接,首先加一层网段/主机限制。只允许特定网段的机器或特定主机访问。但是要注意,客户端的 IP 由客户端自己决定,因此限制访问者的 IP 并不能保证安全。
  2. 为了防止连接从不受控制的网卡发起,限制 Portal. 这只是 Server 自己对自己的限制,避免不处于 SAN 网络中的以太网卡对外提供存储服务。
  3. 为了防止同一个 SAN 网络中不合法的机器连接 Target,要设置 Initiator Group ID,即白名单。但是要注意,Initiator name 也是由发起者自由更改,因此限制 iqn 也不能保证安全。
  4. CHAP 认证,只有拥有正确的 iqn:secret pair 的机器才能发起连接。由于 Initiator 登录的口令被存储在 Server 的 Target 里,因此具有强鉴权能力。但是貌似也不能防范爆破攻击。

如此一来,基本可以保证可访问性、机密性的“万无一失”。而且这个组还可以添加多个,配置非常灵活。

注意:此处有三个 Targets,但是 SAN 拓扑图里只有两个,因为我后面可能要购置一台服务器,所以把 Target 分离开比较好。此处的截图是真实情况。

警告

对于 iSCSI,用户经常配置完之后就正常使用,直到很久以后出现某些需要解决的故障。在重新配置操作系统的时候,不免要重新配置 iSCSI share 表项。Extents 的 logical block size 字段表明了我们希望 iSCSI 客户端每次读写的块大小是多少。这个值与 ZVol 本身没有关系,但是在 Extents 设置时非常重要,如果在重新配置时,没有选择正确的、与初始化 LUN 并创建文件系统时完全一样的 LBS,则客户机无法使用该 LUN. 因此务必要记清楚这个值的大小。

图 12. 设置 Extent 的 Logical Block Size 数值。

1.1.6 配置 Extents·

Extents 在这个语境里很难翻译,如果认为是“程度”则完全不合适。我倾向于认为这就是逻辑硬盘,即 LUN. Extents 可以是某个文件,可以是一个 ZVoL,如果你用的是 TrueNAS CORE,那么 Extents 还可以是一整个物理硬盘。

图 13. Extent 是服务端最底层的概念之一,标识一块逻辑上的硬盘,即 LUN.

1.1.7 将 Extents 与 Targets 相关联·

Targets 后面可以拖一堆 Extents,将某个 Extent 关联到某个 Target,可以让 Initiator 发起连接时直接找到这个 Target 下面的所有 Extents 逻辑盘。

图 14. 一个 Target 可以挂多个 Extent,这一点要牢记。

1.1.8 小结·

由此,我们通过截图、介绍的方式,按照步骤讲解清楚了 iSCSI 块存储共享的配置。许多场景可能需要 RDMA 支持的 iSCSI,但是目前 TrueNAS 还不支持,所以我们把这个问题留到未来解决。

1.2 Debian 12 通用系统 iSCSI 服务端配置·

pass,我现在还不会,也没有需求。

2. iSCSI 客户端配置·

在 Windows Server 以及普通版本 Windows 上设置 iSCSI 客户端非常简单,图形化操作就是爽。但是在 Linux 上,需要对相关命令行非常熟悉。本着解决难题的想法,我们把主要精力放到 Linux 上。

2.1 Debian 12 iSCSI 客户端配置·

熟悉 Linux 软 RAID 的用户对 mdadm(Multiple Devices Administrator) 应该不陌生,配置 iSCSI 的命令行软件与其名字类似:iscsiadm. 要学会使用 iscsiadm,需要对 iSCSI 协议履行过程中的流程非常熟悉,总体来看,大致归为以下五个步骤:

  1. 某 initiator 对存储网络中某个存储服务器(Portal)发起 discovery 请求,试图获取 iscsi-target 列表
  2. Server 通过 Portal 收到 initiator discovery 请求之后,查看 initiator 发送过来的 iqn,对本机所有的 target 进行遍历。target 中存储着下列一组四元组:{Portal_Group_ID, Initiator_Group_ID, Authentication_Method, Authentication_Group_Number}. Server 将允许此 Initiator 访问的 Targets 发送给 Initiator. Target 四元组信息如下:
  • Portal_Group_ID,指的是这个 Target 存储在哪个 Portal 后面。毕竟一个存储服务器可以有多张网卡、接入多个 SAN,因此指定 Portal 很重要。
    • Initiator_Group_ID,initiator Group 里存放了许多客户端的 iqnInitiator_Group 绑定到该 Target,代表只有该组里的 iqn 才有资格查看到这个 Target.
    • Authentication_Method,认证方法,一般是 CHAP.
    • Authentication_Group_Number,填写 Authorized Access,而 Authorized Access 里存储着与客户端 iqn 相关联的 CHAP secret 口令。如果你在客户端里配置了 CHAP secret,那么就需要启用此项:把客户端的 iqn 以及对应 CHAP secret 写入 Authorized Access,再把 Authorized Access ID 写到 Authentication_Group_Number.
  1. initiator 拿到 target 列表,则按照需要登录到特定的 target,从而获得逻辑硬盘。随后 cat /proc/partitions 即可查看多出来的硬盘。

登录到 target 之后,查看硬盘的妥善方法是使用 ls -al /dev/disk/by-id 命令查看输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ ll /dev/disk/by-id
total 0
lrwxrwxrwx 1 root root 13 May 18 11:51 nvme-eui.23f77b718fa2b77d000c29664a4a133a -> ../../nvme0n1
lrwxrwxrwx 1 root root 15 May 18 11:51 nvme-eui.23f77b718fa2b77d000c29664a4a133a-part1 -> ../../nvme0n1p1
lrwxrwxrwx 1 root root 15 May 18 11:51 nvme-eui.23f77b718fa2b77d000c29664a4a133a-part2 -> ../../nvme0n1p2
lrwxrwxrwx 1 root root 15 May 18 11:51 nvme-eui.23f77b718fa2b77d000c29664a4a133a-part3 -> ../../nvme0n1p3
lrwxrwxrwx 1 root root 13 May 18 11:51 nvme-VMware_Virtual_NVMe_Disk_VMware_NVME_0000 -> ../../nvme0n1
lrwxrwxrwx 1 root root 15 May 18 11:51 nvme-VMware_Virtual_NVMe_Disk_VMware_NVME_0000-part1 -> ../../nvme0n1p1
lrwxrwxrwx 1 root root 15 May 18 11:51 nvme-VMware_Virtual_NVMe_Disk_VMware_NVME_0000-part2 -> ../../nvme0n1p2
lrwxrwxrwx 1 root root 15 May 18 11:51 nvme-VMware_Virtual_NVMe_Disk_VMware_NVME_0000-part3 -> ../../nvme0n1p3
lrwxrwxrwx 1 root root 9 May 18 14:54 scsi-1TrueNAS_84f7b4ea-openwrt-code -> ../../sda
lrwxrwxrwx 1 root root 9 May 18 14:54 scsi-36589cfc00000020763aa0adea9471f25 -> ../../sda
lrwxrwxrwx 1 root root 9 May 18 14:54 scsi-STrueNAS_iSCSI_Disk_547034d70b35335 -> ../../sda
lrwxrwxrwx 1 root root 9 May 18 14:54 wwn-0x6589cfc00000020763aa0adea9471f25 -> ../../sda

可以看到 11 行包含 openwrt-code 字样,这正是我日常用来防止 OpenWrt 源代码的逻辑盘。它指向 /dev/sda,我们可以使用 mkfsmount 等命令对其进行格式化、挂载等常规操作。

熟悉完上述流程,我们再学习一下如何使用 iscsiadm.

2.1.1 iscsiadm 软件安装与配置·

Debian 12 系统可以通过下列命令安装 iSCSI 客户端软件:

1
sudo apt install open-iscsi

随后在命令行输入 iscsiadm -V 查看软件版本。一般来说,Debian 12 上游里的 open-iscsi 版本还是比较新的。

随后配置 Initiator iqn 名字:

1
sudo vim /etc/iscsi/initiatorname.iscsi

输入 InitiatorName=iqn.2019-08.cn.littlenewton:epyc-debian,这也是此文件唯一的有效行。

最后配置 iSCSI 相关参数:

1
sudo vim /etc/iscsi/iscsid.conf

将相关行改为如下样式:

1
2
3
4
5
6
node.session.auth.authmethod = CHAP
...
node.session.auth.username = iqn.2019-08.cn.littlenewton:epyc-debian
node.session.auth.password = xxxxxSECRETxxxxx

# discovery 相关的 CHAP 被省略,因为不太必要!

**要重点区分 node 和 discovery 两个不同场景下的认证方法及口令。**不过我认为 discovery 有了 iqn 限制也基本够用了,只要在 session 建立时加一层认证即可。唯一的危险就是当 SAN 网络里存在恶意用户通过穷举 iqn 的方式发起 discovery 时,有泄露 LUN 名字的危险。

2.1.2 使用 iscsiadm 登录到 Portal·

由于我们没有改 iSCSI 服务默认端口,所以只写 IP 地址即可。

1
sudo iscsiadm -m discovery --type sendtargets --portal 10.2.1.33

iscsiadm 使用 mode (-m) 和 operation-type (-type) 组合的方式实现操作。我们登录时的 mode 是 discovery,要求对方给我们发送 Targets 列表。

2.1.3 使用 iscsiadm 登录到某个 Target·

此时使用 node 模式,-T (--targetname) 指代要连接到的 Target,行为是 --login (-l),即登录。

1
sudo iscsiadm -m node -T iqn.2019-08.cn.littlenewton:epyc-truenas:openwrt-code --login

如果要登录到所有已知的 Targets,可以直接写 --loginall (-L).

2.1.4 使用 iscsiadm 修改开机自动挂载·

如果不修改,那么重启之后系统不会自动搜索 Target 并连接,很多服务可能因此无法启动。为此,我们在 discovery 之后,查看

1
sudo cat /etc/iscsi/nodes/iqn.2019-08.cn.littlenewton:epyc-truenas:openwrt-code/10.2.1.33,3260,1/default

会得到一大串输出:

1
2
3
4
5
6
7
# BEGIN RECORD 2.1.3
node.name = iqn.2019-08.cn.littlenewton:epyc-truenas:openwrt-code
node.tpgt = 1
node.startup = manual
node.leading_login = No
...
# END RECORD

这是我们登录到该 Target (Node) 之后,客户端记录到的有关信息。其中第四行 node.startup 为手动,我们需要改成自动,可通过如下命令实现:

1
sudo iscsiadm -m node -T iqn.2019-08.cn.littlenewton:epyc-truenas:openwrt-code -o update -n node.startup -v automatic

如此一来,即可完成开机自动启动。如果对这个硬盘初始化完成(以 ext4 文件系统为例),可以在 /etc/fstab 里添加下面一行代码实现自动挂载(前提是已经创建好挂载点,即 mkdir /home/newton/epyc-truenas-openwrt)。如此一来便于普通硬盘没有区别。

1
/dev/sda  /home/newton/epyc-truenas-openwrt  ext4  _netdev  0  0

2.2 Windows Server iSCSI 客户端配置·

Windows 客户端非常简单。

2.2.1 配置 iscsi 发起程序·

在 Windows 搜索中输入“iSCSI 发起程序”,进入程序。首先点击配置选项卡,将本机的 iqn 按照之前提到的方法修改。

图 15. 设置 Windows 系统的 iqn 名字。

由于我们不需要设置 discovery 验证,因此无需继续配置。

2.2.2 进行目标发现·

图 16. 使用 Window 的 iSCSI 发起程序连接到 iSCSI Target. 细节同样很多。

2.2.3 高级选项·

会话的 CHAP 认证可以在这里找到。如果连接不稳定,可以在连接方式中手动配置。

图 17. 在 Windows 上使用 CHAP 验证同样很简单。