PancrasL的博客

使用SPDK部署iSCSI

2021-03-04

image-20210305102234994

iSCSI

简单的说,iSCSI是一种存储设备远程映射技术,它可以将一个远程服务器上的存储设备映射到本地,并呈现为一个块设备(大白话就是磁盘)。从普通用户的角度,映射过来的磁盘与本地安装的磁盘毫无差异。iSCSI 这个架构主要将储存装置与使用的主机分为两个部分,负责连接的软件成为启动器。而提供存储服务的计算机成为服务端,其中的软件成为目标器

  • iSCSI Target:储存设备端,存放磁盘或 RAID 的设备。
  • iSCSI Initiator:能够使用 Target 的客户端,通常是服务器。

与iSCSI类似的是NFS技术,它们都是为了解决存储资源共享的方案,只不过工作层次不一样,前者在客户机呈现的是一个块设备,后者呈现的是一个目录树。

在这篇文章中,我们将使用SPDK开发工具包的 iscsi_tgt 程序来部署iSCSI。

本文在第二章对部署步骤和常用命令进行介绍 ,第三章将演示一个完整的部署过程。

如果您想获取更详细的信息,请参考官方文档

基本概念

![image-20210305102234994](spdk-iscsi/iSCSI structure.png)

  • Network Portal: 网络端口。网络实体的一个组成部分,它有一个 TCP/IP 地址。 网络端口在 Initiator 用 IP 地址标识, 在 Target 用 IP 地址+侦听的 TCP 端口标识。

  • Session: 连接 Initiator 和 Target 的一组 TCP 连接构成一个 session(可以简单理解为 I_T nexus)。可以向 session 添加 TCP 连接,也可以把 TCP 连接从 session 删除。 也就是说一个session中是可以有多个连接的。通过一个 session 的所有连接,Initiator 只看到同一个 Target。

  • Connection : 一个 TCP 连接。Initiator 和 Target 之间使用一或者多个 TCP 连接通信。

  • CID(Connection ID): 一个 session 里的每个 connection 用 CID 进行标识,该标识在 session 范围内是唯一。CID 由 Initiator 产生,在 login 请求和使用 logout 关闭 连接时传递给 Target。

  • SSID(Session ID):一个 iSCSI Initiator 与 iSCSI Target 之间的会话(Session)由会话ID(SSID)定义,该会话ID是一个由发起方部分(ISID)和目标部分(Target Portal Group Tag)组成的元组。 ISID 在会话建立时由发起者明确指定。 Target Portal Group Tag 由发起者在连接建立时选择的 TCP端口来隐式指定。 当给定 targetName 时,targetPortalGroupTag 也必须由目标在连接建立期间作为确认返回。

  • Portal Groups: 网络端口组。iSCSI session 支持多连接,一些实现能把通过多个端口建立的多个连接捆绑到一个 session。 一个 iSCSI 网络实体的多个网络端口被定义为一个网络端口组,把该组和一个 session 联系起来,该 session 就可以捆绑通过该组内多个端口建立的多个连接,再使它们一起协同工作以达到捆绑的目的。每一个该组的 session 并不需要包括该组的所有网络端口。一个 iSCSI 节点可能有一或者多个网络端口组,但是每一个 iSCSI 使用的网络端口只能属于 iSCSI 节点的一个组。

  • Target Portal Group Tag: 网络端口组标识。使用 16 比特的数标识一个网络端口组。在 一个 iSCSI 节点里,所有具有同样组标志的端口构成一个网络端口组。

  • iSCSI Task: 一个 iSCSI 任务是指一个需要响应的 iSCSI 请求。

  • I_T nexus: I_T nexus 是指一个 SCSI Initiator 的端口和一个 SCSI Target 端口之间 的关系。 对于 iSCSI, 这个关系对应一个 session, 它指 session 的 Initiator 端和 iSCSI Target 网络端口组之间的关系。I_T nexus 的标识是一对端口名称(iSCSI Initiator 名称+i+ISID,iSCSI Target 名称+t+网络端口组标识)。 PDU (Protocol Data Unit): Initiator 和 Target 之间通信时把信息分割为消息。这些 消息称为 iSCSI PDU。 SSID (Session ID): iSCSI Initiator 和 iSCSI Target 之间的 session 用 SSID 进行标识, 该标识由 Initiator 部分的 ISID 和 Target 部分的 TPGT 构成。

  • ISID(The Initiator part of the Session Identifier):发起方会话标识,由 Initiator 在 session 建立的时候明确给出,

  • TSIH (Target Session Identifying Handle): Target 分配给与特定名称 Initiator 建立的 session 的标识。 但是 0 值被保留着用于 Initiator 告知 Target 这是一个新 session。 在为一个 session 添加一个 connect 时,TSIH 已经隐含指明。

iSCSI Target入门指南

我们将使用SPDK开发工具包的 iscsi_tgt 程序来部署iSCSI,该程序位于 build/bin

前期准备

本指南首先假设您已经可以在平台上构建标准SPDK发行版。

构建完成后,二进制文件将位于 build / bin 中。

如果要通过信号杀死应用程序,请确保使用SIGTERM,然后应用程序将在退出前释放所有共享内存资源,SIGKILL将使共享内存资源没有机会被应用程序释放,您可能需要手动释放资源。

介绍

下图显示了本文档中描述的iSCSI结构的不同部分之间的关系.

![image-20210305102234994](spdk-iscsi/iSCSI structure.png)

将 CPU 和 iSCSI Target 绑定

SPDK使用DPDK环境抽象层来访问硬件资源,例如大内存页和CPU内核。 DPDK EAL提供了将线程分配给特定内核的功能。 为确保SPDK iSCSI目标具有最佳性能,请将NIC和NVMe设备放在同一NUMA节点上,然后将目标配置为在与该节点关联的CPU内核上运行。 以下命令行选项用于配置SPDK iSCSI Target:

1
-m 0xF000000

在此示例中,将使用CPU内核24、25、26和27。

通过RPC方法配置iSCSI Target

iSCSI目标是通过JSON-RPC调用配置的,SPDK在 spdk/scripts/rpc.py 提供了一个python脚本用来简化配置。 有关详细信息,请参见JSON-RPC

以下列出常用的几个命令:

Portal groups

  • iscsi_create_portal_group – Add a portal group.
  • iscsi_delete_portal_group – Delete an existing portal group.
  • iscsi_target_node_add_pg_ig_maps – Add Initiator group to portal group mappings to an existing iSCSI Target node.
  • iscsi_target_node_remove_pg_ig_maps – Delete Initiator group to portal group mappings from an existing iSCSI Target node.
  • iscsi_get_portal_groups – Show information about all available portal groups.
1
$ sudo scripts/rpc.py iscsi_create_portal_group 1 10.0.0.1:3260

注意,这里的ip地址时你的taget主机的ip地址,端口是一个未被占用的端口

Initiator groups

  • iscsi_create_initiator_group – Add an Initiator group.
  • iscsi_delete_initiator_group – Delete an existing Initiator group.
  • iscsi_initiator_group_add_initiators – Add initiators to an existing Initiator group.
  • iscsi_get_initiator_groups – Show information about all available Initiator groups.
1
$ sudo scripts/rpc.py iscsi_create_initiator_group 2 ANY 10.0.0.2/32

注意,这里的ip地址是你的client主机的ip地址

Target nodes

  • iscsi_create_target_node – Add an iSCSI Target node.
  • iscsi_delete_target_node – Delete an iSCSI Target node.
  • iscsi_target_node_add_lun – Add a LUN to an existing iSCSI Target node.
  • iscsi_get_target_nodes – Show information about all available iSCSI Target nodes.
1
$ sudo scripts/rpc.py iscsi_create_target_node target3 target3_alias MyBdev:0 1:2 64 -d

配置iSCSI启动器

Linux启动器是open-iscsi。

1
2
3
4
5
6
7
8
9
# 安装open-iscsi

# 软件包Fedora:

$ yum install -y iscsi-initiator-utils

# Ubuntu:

$ apt install -y open-iscsi

发现

假设taget主机的ip地址是 10.0.0.1

1
$ iscsiadm -m discovery -t sendtargets -p 10.0.0.1

连接到Target

1
$ iscsiadm -m node --login

此时,iSCSI目标应显示为SCSI磁盘。检查dmesg以查看它们出现的情况。

断开与Target的连接

1
$ iscsiadm -m node --logout

删除Target节点缓存

1
$ iscsiadm -m node -o delete

这将导致启动器忘记所有先前发现的iSCSI目标节点。

查找iSCSI LUN的/ dev / sdX节点

1
$ iscsiadm -m session -P 3 | grep "Attached scsi disk" | awk '{print $4}'

这将显示所有已登录iSCSI会话中每个SCSI LUN的/ dev节点名称。

示例: 配置简单的iSCSI Target

本示例演示挂载nvme磁盘,如果没有磁盘的话可以参考官方教程挂载内存模拟的块设备。

假设我们在Target端拥有一块nvme磁盘,位于192.168.0.22,现在想通过配置iSCSI让192.168.0.21主机访问到该磁盘。

前期准备

通过VMware在Target端挂载一块5GB的NVMe硬盘,并将控制权移交给SPDK

![nvme ssd](spdk-iscsi/nvme ssd.png)

  • 在Target端查看挂载情况
1
2
3
$ ls /dev | grep nvme
nvme0
nvme0n1
  • 启动SPDK,指示操作系统放弃控制设备,让SPDK控制设备
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$ cd spdk

# 运行脚本转让设备控制权给SPDK
$ sudo /scripts/setup.sh
0000:03:00.0 (15ad 07f0): nvme -> uio_pci_generic

# 查看设备状态
$ sudo scripts/setup.sh status
Hugepages
node hugesize free / total
node0 1048576kB 0 / 0
node0 2048kB 1024 / 1024

Type BDF Vendor Device NUMA Driver Device Block devices
NVMe 0000:03:00.0 15ad 07f0 0 uio_pci_generic - -

# 此时设备将从/dev目录下消失
$ ls /dev | grep nvme
# 无输出信息

配置iSCSI Target

  • 启动 iscsi_tgt 应用
1
$ sudo ./build/bin/iscsi_tgt
  • 创建ID为1的新的端口组,地址为 192.168.0.21:3111
1
$ sudo ./scripts/rpc.py iscsi_create_portal_group 1 192.168.0.22:3111
  • 创建一个ID为2的启动器组以接受来自0.0.0.0/0的任何连接:
1
$ sudo ./scripts/rpc.py iscsi_create_initiator_group 2 ANY 0.0.0.0/0
  • 在SPDK中基于NVMe设备创建块设备(bdev)

也可以基于其他设备创建bdev,详见 https://spdk.io/doc/bdev.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 获取NVMe设备的地址
$ sudo ./scripts/setup.sh status
Hugepages
node hugesize free / total
node0 1048576kB 0 / 0
node0 2048kB 655 / 1024

Type BDF Vendor Device NUMA Driver Device Block devices
NVMe 0000:03:00.0 15ad 07f0 0 uio_pci_generic - -

# 连接本地PCIe驱动器,在系统中创建物理设备的NVMe bdev
$ sudo ./scripts/rpc.py bdev_nvme_attach_controller -b NVMe1 -t PCIe -a 0000:03:00.0
NVMe1n1

# 查看块设备
$ sudo ./scripts/rpc.py bdev_get_bdevs
...
"name": "NVMe1n1",
"aliases": [],
...

  • 使用先前创建的bdev构建一个目标,如LUN0(NVMe1n1),名称为“myNVMeDisk1”,别名为“My NVMe Disk1”,使用portal group 1和Initiator group 2。
1
$ sudo ./scripts/rpc.py iscsi_create_target_node myNVMeDisk1 "My NVMe Disk1" "NVMe1n1:0" 1:2 64 -d

配置 Initiator

  • 发现 Target
1
2
$ sudo iscsiadm -m discovery -t sendtargets -p 192.168.0.22:3111
192.168.0.22:3111,1 iqn.2016-06.io.spdk:myNVMeDisk1
  • 连接Target
1
$ sudo iscsiadm -m node –T iqn.2016-06.io.spdk:myNVMeDisk1 --login

此时,iSCSI Target应该显示为SCSI磁盘。

  • 使用 dmesg 命令以查看它们的状态。 在此示例中,它可能如下所示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ dmesg
...

[ 3032.069633] Loading iSCSI transport class v2.0-870.
[ 3032.096151] iscsi: registered transport (tcp)
[ 3096.810163] scsi host33: iSCSI initiator over TCP/IP
[ 3096.819259] scsi 33:0:0:0: Direct-Access INTEL NVMe disk 0001 PQ: 0 ANSI: 5
[ 3096.820165] sd 33:0:0:0: Attached scsi generic sg3 type 0
[ 3096.822238] sd 33:0:0:0: [sdb] 10485760 512-byte logical blocks: (5.37 GB/5.00 GiB)
[ 3096.822470] sd 33:0:0:0: [sdb] Write Protect is off
[ 3096.822472] sd 33:0:0:0: [sdb] Mode Sense: 83 00 00 08
[ 3096.822814] sd 33:0:0:0: [sdb] Write cache: disabled, read cache: disabled, doesn't support DPO or FUA
[ 3096.823122] sd 33:0:0:0: [sdb] Optimal transfer size 4194304 bytes
[ 3096.829189] sd 33:0:0:0: [sdb] Attached SCSI disk

...
  • 还可以使用简单的bash命令在所有记录的iSCSI会话中为每个iSCSI LUN查找 /dev/sdX 节点:
1
2
$ sudo iscsiadm -m session -P 3 | grep "Attached scsi disk" | awk '{print $4}'
sdb

Reference
[1]: https://zhuanlan.zhihu.com/p/60986068
[2]: https://spdk.io/doc/iscsi.html