iSCSI 块存储实战:给网站服务器挂一块远程磁盘,实现计算与存储分离的完整方案

为什么个人站长会用到 iSCSI:把「盘」从机器里搬出来

大部分个人站长的服务器只有一块系统盘,网站数据、数据库、日志全堆在上面。盘写满了要扩容,只能停机、挂新盘、迁移数据,运气不好还得重装系统。如果你手上有多台 VPS,还会遇到另一个更麻烦的问题:老机器磁盘只剩 3G,新机器空着 200G,但网站跑在老机器上,想用新机器的空间根本用不上。

iSCSI 解决的正是这类问题。它把 远端的磁盘通过网络「搬」到本机,本机看到的是一块普通的块设备(比如 /dev/sdb),可以像本地盘一样分区、格式化、挂载、写文件。区别只在于:数据实际落在另一台机器(或 NAS)上。对操作系统而言它就是一块裸盘,对你的应用而言完全无感。

这不是什么新东西,企业里用了几十年,但它对个人站长有个很实际的价值:把存储和计算分开。计算便宜的机器(CPU 好、内存大)负责跑站,存储便宜的机器(盘大、带宽足)专门存数据。哪天计算机器想换、想重装,数据都还在存储机上,拔掉重连即可。

下面我把整套流程从概念讲到落地,最后给一套「低配 VPS + 一台大盘机」的个人站可用方案,并列出几个必须知道的坑。

先搞清楚三个角色:initiator、target、LUN

iSCSI 的名字来自 SCSI over IP,本质是把 SCSI 命令封装进 TCP 包里传。整套架构里只有三个概念要记:

  • target(目标端):提供磁盘的一方,也就是「存储服务器」。它把本机的一块盘、一个分区、一个 LVM 卷,甚至一个文件,导出成一个 LUN。
  • initiator(发起端):使用磁盘的一方,也就是「网站服务器」。它通过网络连到 target,把远程 LUN 认成本地一块 SCSI 磁盘。
  • LUN(Logical Unit Number):target 导出的逻辑单元,一块 LUN 对应发起端看到的一块盘。一个 target 可以挂多个 LUN。

你可以把它类比成「网络版的 USB 移动硬盘」:target 是那块硬盘,initiator 是插上硬盘的电脑。不同点是它走以太网、支持多台机器同时连接(但共享写要非常小心,见后文)。

和 NFS、Samba 这类文件级共享相比,iSCSI 是块级的:文件级共享里,客户端看到的是文件夹、走的是文件协议,多台机器可以同时读写同一批文件,冲突由服务端协调;块级共享里,客户端看到的是整块裸盘,操作系统自己在上面建文件系统,因此默认情况下同一块 LUN 不能被两台机器同时挂载——两边各自维护自己的文件系统元数据,同时写必然把盘写坏。这一点后面会再强调。

target 端:用 targetcli 导出一块盘

target 端我推荐用 Linux 内核自带的 LIO(targetcli),Debian/Ubuntu 下一条命令装好:

apt update
apt install -y targetcli-fb

systemctl enable --now rtslib-fb-targetctl 2>/dev/null || systemctl enable --now target
systemctl status target --no-pager

装好后运行 targetcli 进入交互 shell。整套配置就三样东西:后端存储(backstore)→ LUN → 门户(portal),外加访问控制(ACL)。

假设我要把 /dev/sdb1 这块 500G 的分区导出(生产上更推荐导出一个 LVM 逻辑卷,方便以后扩容),流程如下:

targetcli
# 1. 创建后端存储,命名为 disk1,指向真实块设备
/backstores/block create disk1 /dev/sdb1

# 2. 创建 target,命名规则是 iqn.年份-月份.反写域名:自定义标识
/iscsi create iqn.2026-10.com.zz1984:storage

# 3. 在 target 下挂 LUN,编号 0
/iscsi/iqn.2026-10.com.zz1984:storage/tpg1/luns create /backstores/block/disk1

# 4. 创建 ACL,只允许指定 initiator 名称连接
/iscsi/iqn.2026-10.com.zz1984:storage/tpg1/acls create iqn.2026-10.com.zz1984:web01

# 5. 创建门户,监听地址与端口(默认 3260)
/iscsi/iqn.2026-10.com.zz1984:storage/tpg1/portals create 0.0.0.0 3260

# 6. 保存配置(关键!不保存重启就没了)
saveconfig
exit

几点说明:

  • IQN 命名是 iSCSI 的全局唯一名,格式固定为 iqn.<年-月>.<反写域名>:<标识>。发起端和 target 各有一个,名字随便起但要全网唯一。用域名反写是为了避免撞名。
  • ACL 是必须的。不加 ACL,任何知道 target 地址和 IQN 的人都能连上你的盘——相当于把硬盘挂到公网上裸奔。ACL 里填的是发起端的 IQN。
  • 门户监听 0.0.0.0:3260 会让它监听所有网卡。生产上更稳妥的做法是只监听内网网卡 IP,或者干脆用防火墙限制来源 IP(见后文)。
  • saveconfig 一定要执行,否则配置只存在于内存,重启即失。LIO 默认把配置存在 /etc/rtslib-fb-target/ 下。

initiator 端:连上远程盘

发起端装 open-iscsi:

apt install -y open-iscsi
systemctl enable --now iscsid

先修改发起端名称,让它等于你在 target ACL 里填的那个 IQN:

# /etc/iscsi/initiatorname.iscsi
InitiatorName=iqn.2026-10.com.zz1984:web01

然后发现并登录 target:

# 发现目标(假设存储机内网 IP 是 10.0.0.5)
iscsiadm -m discovery -t sendtargets -p 10.0.0.5:3260

# 登录(-m node 表示对已发现的节点操作)
iscsiadm -m node -T iqn.2026-10.com.zz1984:storage -p 10.0.0.5:3260 -l

# 查看会话
iscsiadm -m session -P 3 | head -40

# 看新出现的块设备
lsblk

登录成功后,lsblk 里会多出一块盘,名字通常形如 sdb,型号里带 LIO-ORG 字样。此时它还是一块裸盘,需要分区、格式化、挂载:

mkfs.ext4 /dev/sdb
mkdir -p /data
mount /dev/sdb /data
df -h /data

到这一步,你的网站服务器就用上了另一台机器的磁盘。把 Nginx 的站点目录、MySQL 的 datadir、或者备份目录指到 /data 下,就实现了「计算存储分离」。

开机自动挂载:_netdev 是关键,不然会卡死

iSCSI 盘是网络设备,开机时网络可能还没就绪。如果直接在 /etc/fstab 里写 /dev/sdb /data ext4 defaults 0 0,开机时挂载会失败,严重时系统卡在紧急模式进不去。

正确写法是加上 _netdev(告诉 systemd 等网络就绪再挂)和 nofail(挂不上也不阻塞启动):

# /etc/fstab
/dev/sdb  /data  ext4  _netdev,nofail,defaults  0  0

更稳的做法是用 LVM。远程 LUN 直接用 /dev/sdb 这种名字有个隐患:服务器插拔过其他磁盘、或加了块网卡,盘符可能漂移,sdb 变成 sdc,挂载就错位了。所以推荐在 LUN 上建一个 LVM 卷组:

pvcreate /dev/sdb
vgcreate vgdata /dev/sdb
lvcreate -l 100%FREE -n lvweb vgdata
mkfs.ext4 /dev/vgdata/lvweb
# fstab 里写 /dev/vgdata/lvweb 而不是 /dev/sdb

LVM 的好处还有两点:一是以后 target 端扩容 LUN,这边 pvresize + lvextend 就能在线扩大,不用停服;二是卷名稳定,不受盘符漂移影响。

另外要确保 open-iscsi 服务开机自启,且它会自动重连上次登录过的会话——登录信息保存在 /etc/iscsi/nodes/ 下,只要 iscsid 起来就会自动登录。

性能优化:让远程盘别拖慢网站

iSCSI 走网络,延迟天然比本地盘高。同一个机房内网(延迟 0.2ms 以内)一般没问题,跨机房就要小心了。几个调优方向:

  • 走内网,别走公网。内网延迟低、带宽足、还免费。公网 iSCSI 既慢又不安全,除非有 IPsec/WireGuard 加密隧道。
  • 用 Jumbo Frame(巨帧)。把 MTU 从 1500 提到 9000,减少网络包数量和 CPU 中断。需要两端网卡和中间交换机都支持,改法:ip link set eth0 mtu 9000,并写入 /etc/network/interfaces 持久化。
  • 调大 iSCSI 读缓存。/etc/iscsi/iscsid.conf 里把 node.session.cmds_max 提到 1024,node.session.queue_depth 提到 128,能让并发 I/O 更顺畅。
  • 多路径(multipath)。如果存储机和网站机之间有多条网线,可以用 multipath-tools 做链路冗余和负载均衡,一块 LUN 走两条路,一条断了自动切换。
  • 数据库慎放远程盘。MySQL/PostgreSQL 对小随机 I/O 的延迟非常敏感,放在远程 iSCSI 上可能比本地 SSD 慢好几倍。真要用,务必先压测(fio 跑一下随机读写)。更常见的做法是:数据库留本地,日志、备份、静态文件、图片这类大块顺序 I/O 放 iSCSI。

五个必踩的坑

坑一:一块 LUN 同时挂两台机器,文件系统直接报废。 这是最常见的致命错误。iSCSI 是块设备,不是文件共享。两台机器同时挂同一块 LUN,双方各自在内存里缓存目录元数据,写下去互相覆盖,用不了多久整个文件系统就变成一堆损坏的块,fsck 都救不回来。如果你确实需要多台机器共享存储,用 NFS/Samba(文件级),或者用集群文件系统(GFS2、OCFS2、GlusterFS),千万别裸挂 iSCSI。

坑二:没加 ACL,盘在公网裸奔。 只设了 portal 却没建 ACL,等于谁都能 iscsiadm -l 连上来。更糟的是没限源 IP,攻击者连上来后可以读到你的全盘数据。务必:ACL 只放行指定的发起端 IQN,并用防火墙限制 3260 端口只对存储机内网 IP 开放。

坑三:忘记 saveconfig,重启配置全丢。 targetcli 的配置如果不 saveconfig,只存在内存里,机器一重启 target 就消失了,发起端会话全部断开。养成习惯:每次改完配置立刻 saveconfig。

坑四:网络抖一下,网站就 502。 iSCSI 盘依赖网络,一旦存储机重启、网线松了、交换机抽风,挂载点会变成「无法访问」状态,Nginx 读写 /data 下的文件直接报错。对策:iscsid.conf 里把 node.session.timeo.replacement_timeout 设小一点(默认 120 秒,可改为 15 秒),让 I/O 快速报错而不是卡死;同时用监控盯着存储机的可达性。对可用性要求高的话,就上多路径 + 两台存储机做主备。

坑五:拿 iSCSI 当备份,等于没备份。 很多人把 iSCSI 盘当「异地备份盘」,但数据其实还是实时挂载着、在被应用读写。存储机要是中了勒索病毒或被人删库,备份一起完蛋。真正的备份要离线和多副本——iSCSI 顶多算「扩展磁盘」,不能替代备份策略。

个人站的低成本组合方案

把上面拼起来,一个很实用的个人站架构是这样的:

  1. 买一台便宜大盘机(比如 4 核 8G、500G 硬盘,价格往往比同样配置小盘机还低),当 target 存储机。它只跑 iSCSI target,不做别的。
  2. 网站机(CPU 好、内存大的小盘 VPS)当 initiator,通过内网连上存储机的一块 LUN。
  3. LUN 在网站机上建成 LVM 卷 /dev/vgdata/lvweb,挂到 /data,Nginx 站点目录、图片、附件、日志全放这里。
  4. 数据库 datadir 留在网站机本地盘,保证低延迟。
  5. 每天用 mysqldump 把数据库导出到 /data/backup,再用 rclone/rsync 同步一份到另一处离线存储。

这套方案的收益很实在:网站机想换、想重装、想升级配置,/data 里的数据一行不动,新机器连上 iSCSI 就能接着跑;存储机扩容硬盘,LUN 扩大后在线 pvresize 即可。计算和存储各自升级,互不牵制。

iSCSI 不是什么高级黑科技,它只是把「盘」这个最基础的东西从物理机箱里解耦出来。对个人站长来说,理解它最大的意义不是立刻去搭一套,而是知道:当磁盘成了瓶颈、当多台机器需要共享空间、当你想把数据从某台机器上「解放」出来时,你手里多了一个成熟、免费、稳定了几十年的方案。

Last modification:October 5th, 2026 at 09:25 pm

Leave a Comment