坑边闲话:iSCSI 块共享协议在数据中心里应用非常广泛,但是许多 NAS 玩家只会在群晖、TrueNAS 等系统上使用 GUI 进行配置。其实在标准的通用 Linux 上配置 iSCSI server 非常简单,理解了原理之后,配置所需的实际步骤非常少。本文详细介绍如何在 Debian 12 系统上配置 iSCSI target.

iSCSI 服务端被称为 target,在 Linux 生态中有不同的 target 代码实现,本文推荐使用现代内核支持的 LIO(Linux-IO Target),这是官方主流方案,稳定、灵活、内核级支持。

常见 target 不同实现的特性如下表所示。

实现方式 服务名称 管理工具 备注
LIO (Linux-IO Target) 内核模块,服务名 rtslib-fb-targetctl targetcli / targetcli-fb 默认、内核集成、现代系统首选
TGT (SCSI Target Framework) tgt 服务 tgtadm, tgt-admin 老牌项目,用户态,简单轻量,但不再积极维护
SCST scst 内核模块 + 用户空间管理工具 scstadmin 高性能、支持更复杂的 IO 策略,但配置复杂
IET (iSCSI Enterprise Target) 已废弃 iscsitarget 编辑 /etc/ietd.conf Debian 已弃用,不推荐使用

从功能深度、性能调优能力上来说,SCST 确实比 LIO 更强一些,但也更复杂、更偏向专业/高性能场景。然而 SCST 仅以内核模块形式提供,由于笔者的 ZFS 已经是内核模块形式,因此笔者不打算引入过多的存储内核模块。

有关

  • iSCSI
  • ISER (iSCSI Extensions for RDMA)
  • SRP (SCSI RDMA Protocol)

客户端的区别,参见下图。

1
2
3
4
5
6
7
8
普通 iSCSI: 基于 TCP/IP 传统网络的 iSCSI
App → SCSI → iSCSI → TCP/IP → NIC

iSER: 对 iSCSI 的网络部分用 RDMA 网络做了替换
App → SCSI → iSCSI → **RDMA** → RDMA NIC

SRP: 直接用 RDMA 网络传输 SCSI 指令
App → **SCSI over RDMA** → RDMA NIC

协议底层

SCSI 命令一般以 CDB(Command Descriptor Block) 形式发送。CDB 是一段结构化的数据,格式通常为 6、10、12 或 16 字节,具体内容因命令类型而异。

典型的 CDB 包含:

  • 操作码(Opcode):1 字节,表示命令类型。
  • 参数字段:比如逻辑块地址、传输长度等。
  • 控制字段:一般用于标志和状态控制。

按用途分类,大致有以下几种:

  • 数据传输类
    • READ(10)
    • WRITE(10)
    • READ CAPACITY
  • 控制类
    • TEST UNIT READY 检查设备是否准备好(通电、就绪)
    • START STOP UNIT 控制设备启动或停止
    • INQUIRY 获取设备基本信息(厂商、型号等)
  • 诊断与维护类
    • REQUEST SENSE 请求错误信息
    • MODE SENSE 获取设备模式参数
    • MODE SELECT 设置设备模式参数

一般来说,SCSI 通信流程如下:

  • 主机(Initiator) 发送 SCSI 命令(CDB)到设备(Target)
  • 设备解析 CDB,执行命令
  • 如果是数据传输命令,会通过 DMA 或 PIO 传输数据
  • 设备返回命令状态(成功 / 错误)
  • 若出错,主机可发 REQUEST SENSE 获取具体错误信息

由此可见,initiator/target 组合不仅仅在 iSCSI 中使用,任何可以被抽象为块设备的 device 都可以被认定为 target,任何连接到块设备的客户端都可以被认定为 initiator. 常见的 target 可以是硬盘、光盘、磁带库、虚拟存储等。

1. 安装必要组件·

执行下列命令即可完成 target 所需组件的安装。

1
sudo apt install targetcli-fb

2. 配置 target·

2.1 术语概览·

  • target: 连接目标,即 iSCSI 服务器
  • initiator: 连接发起人,即 iSCSI 客户端

2.2 配置具体的 target·

本节通过一个具体的案例展示如何配置 target.

配置 LIO 的 target 需要使用 targetcli 命令行工具,该工具可以支持

  • targetcli 提供的环境里交互式执行;
  • 也可以通过 targetcli 后缀指令的方式在 shell 中逐条执行。

本文以交互式方式展示。执行下列代码进入交互式环境

1
sudo targetcli

随后按照注释分别执行各个步骤,完成后端存储存储创建、target 创建、门户监听、认证、ACL 鉴权等操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# --------------------------------------------------------------------------------------
# 步骤 1: 配置后端存储 (Backstore)
# --------------------------------------------------------------------------------------

# 创建一个名为 "dropbox" 的块设备后端存储,并关联到指定的 ZFS 卷。
/backstores/block create name=dropbox dev=/dev/zvol/Samsung_990Pro_Stripe/dropbox

# --------------------------------------------------------------------------------------
# 步骤 2: 配置 iSCSI Target
# --------------------------------------------------------------------------------------

# 创建一个 iSCSI Target,并为其指定全局唯一的 IQN 名称 (服务器的身份标识)。
/iscsi create iqn.2019-08.cn.littlenewton:gtr7-debian

# --------------------------------------------------------------------------------------
# 步骤 3: 在 Target 中创建 LUN、Portal 和 ACL
# --------------------------------------------------------------------------------------

# 在 Target 的 TPG1 中创建一个 LUN (默认为 LUN 0),并将其链接到之前创建的 "dropbox" 后端存储。
/iscsi/iqn.2019-08.cn.littlenewton:gtr7-debian/tpg1/luns create /backstores/block/dropbox

# 创建一个网络门户 (Portal),让 Target 在所有 IPv4 地址的 3260 端口上监听连接请求。
/iscsi/iqn.2019-08.cn.littlenewton:gtr7-debian/tpg1/portals create 0.0.0.0 3260

# 创建一个访问控制列表 (ACL),只允许来自指定 IQN (客户端 "gtr7-win10") 的连接。
/iscsi/iqn.2019-08.cn.littlenewton:gtr7-debian/tpg1/acls create iqn.2019-08.cn.littlenewton:gtr7-win10

# 设置 TPG 的属性:关闭 CHAP 认证,意味着连接验证完全依赖于上面的 ACL (IQN 白名单)。
/iscsi/iqn.2025-07.com.example:dropbox/tpg1 set attribute authentication=0

# 将 TPG1 中的 LUN 0 映射给这个特定的 ACL,这样授权的客户端才能真正看到这个磁盘。
/iscsi/iqn.2019-08.cn.littlenewton:gtr7-debian/tpg1/acls/iqn.2019-08.cn.littlenewton:gtr7-win10 create mapped_lun=0 tpg_lun=0

# --------------------------------------------------------------------------------------
# 步骤 4: 保存配置
# --------------------------------------------------------------------------------------

# 保存当前配置,使其在系统重启后依然生效。
saveconfig

接下来详细解析每一步的具体含义。

2.2.1 准备后端存储·

使用 /backstores 路径(即命令)管理后端存储。后端存储一般指的是本地的某个块设备,比如

  • 磁带机,
  • ZFS 的某个 ZVol,
  • LVM 的某个 LV,
  • 本地的某个具体的 HDD, SSD 等。

只要该设备是块设备,即可通过 iSCSI 共享出去。前文已经有所提及,本处不再赘述。

targetcli 支持四种常见的块设备后端,具体解释如下:

  • block
    • 使用系统中已经存在的块设备,性能好、直通底层块设备。
    • 命令:/backstores/block create name=myvol dev=/dev/sdb
  • fileio
    • 使用一个普通的文件作为 LUN,性能一般,但是灵活。
    • 命令:/backstores/fileio create name=myfile file_or_dev=/var/iscsi/mydisk.img size=10G
  • pscsi
    • 使用已有的 SCSI 设备(Pass-through SCSI),把现有的 SCSI 设备转发给 initiator,通常用于磁带库或光驱。设备必须支持并允许 SCSI 命令 pass-through.
    • 命令:/backstores/pscsi create name=psdev dev=/dev/sgX
  • ramdisk:
    • 内存盘,一般是测试使用。在内存中创建一个虚拟设备,所有数据存在内存中,重启即丢失。。
    • 命令:/backstores/ramdisk create name=ram0 size=1G

2.2.2 设置 iSCSI target·

我们先想象一个更复杂的、高可用的存储环境:

  • 你有一台存储服务器,但它有两个控制器(Controller A 和 Controller B),以防其中一个坏掉。
  • 每个控制器都有自己的网络端口。
  • 客户端(Initiator)可以看到通往同一个 LUN(磁盘)的多条路径(Path),一条通过 Controller A,另一条通过 Controller B。

这时问题来了:客户端应该使用哪条路径?两条路径的性能和状态一样吗?

ALUA 就是用来解决这个问题的。 它允许存储目标器(target)告诉客户端(initiator)每一条路径的状态。常见的状态有:

  • Active/Optimized (活动/优化): 这是首选路径。读写速度最快,延迟最低。
  • Active/Unoptimized (活动/非优化): 这条路径也能用,但是性能较差(例如,数据需要通过控制器之间的内部链接转发)。它通常作为备用路径。
  • Standby (备用): 这条路径当前不可用,但如果主路径失效,它可以被激活。
  • Unavailable (不可用): 路径已断开或有故障。

而 tpg(Target Port Group, 目标端口组)就是实现这个机制的工具。 服务器将所有具有相同路径状态的目标端口(Portals)放到同一个组里。例如:

  • tg_pt_gp_A (我们自己命名的组) 可能包含 Controller A 上的所有端口,状态为 Active/Optimized。
  • tg_pt_gp_B 可能包含 Controller B 上的所有端口,状态为 Active/Unoptimized。

当客户端连接时,它会查询这些组的状态,然后智能地选择最优的路径(Active/Optimized)来发送 I/O 请求,同时知道如果主路径失效,可以切换到次优路径(Active/Unoptimized)。

即使在本文描述的如此简单的单路径环境中:

  • 只有一台服务器。
  • 只有一个 iSCSI Target。
  • 只有一个 TPG (tpg1)。
  • 只有一个 Portal (0.0.0.0:3260)

LIO Target(Linux 的 iSCSI 内核模块)仍然使用 ALUA 框架来描述路径状态。

2.2.3 设置 iSCSI ACL·

ACL(Access Control List,访问控制列表)用于控制哪些 initiator(发起端)有权限访问指定的 target(目标端)。iSCSI 默认采用“白名单”策略:只有被明确列入 ACL 的 initiator 名称,才被允许连接到对应的 target. 若 ACL 为空,则代表拒绝所有 initiator 连接该 target(即默认拒绝)。为了增强安全性,通常需要结合 CHAP(Challenge-Handshake Authentication Protocol)认证机制共同使用。

需要注意的是,这里的 ACL 控制 并不涉及身份验证,仅仅是基于 Initiator IQN 的访问限制。这意味着安全性有限,因为 initiator 名称是可以在客户端随意伪造或修改的,因此依赖 ACL 并不能提供真正的安全隔离。建议在生产环境中始终搭配 ACL 和 CHAP 双重机制,以避免伪造 initiator 名称所带来的潜在风险。

2.2.4 最终效果展示·

图 1. targetcli 最终效果。

总结·

本文详细介绍了在 Linux 上部署 Linux-IO 类型的 iSCSI target 的全过程。

由于服务器性能比较复杂而且不需要特别高的性能,因此本文忽略了 benchmark 展示部分。实际上,本文所使用的软硬件架构非常复杂,不属于常规操作。

  • 宿主机:零刻 GTR7
  • CPU & Mem: AMD 7840HS, 32GB DDR5 5600MHz SODIMM *2
  • 硬盘:Samsung 990 Pro 4TB
  • 宿主机系统 & 虚拟化方案:Windows 10 Pro, Hyper-V
  • 虚拟机:Debian 12
  • 虚拟机磁盘:使用 Hyper-V 物理磁盘,将 Samsung 990 Pro 4TB 映射给虚拟机使用。

在该场景下,Windows 10 宿主机通过 Hyper-V 网络连接虚拟机的 iSCSI targe,存储吞吐可以达到 1.1GB/s 以上,足以满足 Dropbox 网盘的文件存储需求。不过后来在升级了 ZFS、宿主机之后,吞吐性能有很大下降,由于缺乏控制变量的条件,所以暂且不做深入分析