Qemuイメージのオーバーレイ(差分)機能を利用した、多数同時アクセス可能な、iSCSIターゲットのホスティングの覚え書き

# はじめに、どういったケースに役立つのか?
この記事は多数のPCを管理するケースで、
「同じ内容のドライブを多数PCで使用する」
「簡単にドライブの変更を一括適応したい」
場合に

「単純な共有ドライブでは、同時アクセスにより、ログイン情報や一時データなどの読み書きに、競合等の問題が起こる」
「共有ドライブへのインストールは、インストーラがコケる、ディレクトリの選択が出来ない」
等の問題も解決し

「複数PCへ差分を一括適応したいが、同期処理がネック」
「同じデータを複数PCに持たせるのは無駄なのでまとめたい」
ニーズも満たします

例としては、ゲーミングPCを多数稼働させたいケースにて、
「ゲームの更新に大容量のダウンロードが発生する」
「複数台同時に更新なんぞすれば回線がパンクしかねない」
「同じ内容の更新なのに台数分のダウンロードは無駄」
「一度の操作でアップデートを一括適応したい」
場合に、一括で更新を適応できます

一括管理の際は、マスターのQemuイメージをiSCSI経由で更新します
(Windows機でマウントして更新を掛ける)



# とりあえず参考URLとその抜粋

## iSCSIターゲットを作成する
https://www.hiroom2.com/2018/05/05/ubuntu-1804-tgt-ja/
>### ファイルをiSCSIターゲットとして使用
> ```
>sudo mkdir /var/lib/iscsi
>sudo dd if=/dev/zero of=/var/lib/iscsi/disk bs=1M count=1K
>```
>### Target ID 1のiSCSIターゲットを作成
>```
>sudo tgtadm --lld iscsi --op new --mode target --tid 1 -T iqn.2018-05.com.hiroom2:disk
>```
>### Target ID 1のiSCSIターゲットにLogical unit 1のロジカルユニットを追加
>ロジカルユニットはブロックデバイスでも可
>```
>sudo tgtadm --lld iscsi --op new --mode logicalunit --tid 1 --lun 1 -b /var/lib/iscsi/>disk
>```
>### Target ID 1のiSCSIターゲットをすべてのIPアドレスに対して公開
>ALLの他に192.168.11.1や192.168.11.0/24等が指定可
>```
>sudo tgtadm --lld iscsi --op bind --mode target --tid 1 -I ALL
>```
>### iSCSIターゲットの設定を保存
>保存しない場合はtgtd再起動時に設定が破棄される
>```
>tgtadm --mode target --op show 
>```

## オーバーレイストレージイメージ
https://linux.die.net/man/8/tgtadm

一度ストレージメディアを作成してから ('backing'イメージ)、QEMUにイメージへの変更をoverlayイメージとして維持させる事が可能
これによりストレージメディアを前の状態に戻せる
戻りたい時点で、オリジナルのbackingイメージを元に新しいoverlayイメージを作成する事で戻す事が出来る

>### overlayイメージを作成
>```
>qemu-img create -o backing_file=img1.raw,backing_fmt=raw -f qcow2 img1.cow
>```
>backingイメージには変更が加えられずストレージへの追記はoverlayイメージファイルに保存される様になる
>backingイメージのパスが変更された場合修正が必要になる 

>### qcow2オーバーレイイメージからパーティションをマウント
>NBD(NetworkBlockDevice)プロトコルを使いディスクイメージを共有する事が可能
>>#### qemu-nbd を使用する為にnbdモジュールをロード
>>```
>>modprobe nbd nbds_max=20
>>```
>
>>#### ディスクを共有してデバイスエントリを作成
>>```
>>qemu-nbd -c /dev/nbd0 /path/to/image.qcow2
>>```
>
>>#### パーティションを検出
>>```
>>partprobe /dev/nbd0
>>```
>
>>#### fdiskを使用してnbd0内のさまざまなパーティションに関する情報を取得
>>```
>>fdisk -l /dev/nbd0
>>```
>>>```
>>>Disk /dev/nbd0: 25.2 GiB, 27074281472 bytes, 52879456 sectors
>>>Units: sectors of 1 * 512 = 512 bytes
>>>Sector size (logical/physical): 512 bytes / 512 bytes
>>>I/O size (minimum/optimal): 512 bytes / 512 bytes
>>>Disklabel type: dos
>>>Disk identifier: 0xa6a4d542
>>>Device      Boot   Start      End  Sectors  Size Id Type
>>>/dev/nbd0p1 *       2048  1026047  1024000  500M  7 HPFS/NTFS/exFAT
>>>/dev/nbd0p2      1026048 52877311 51851264 24.7G  7 HPFS/NTFS/exFAT
>>>```
>
>>#### ドライブイメージの任意のパーティションをマウント
>>例えばパーティション2をマウントする
>>```
>>mount /dev/nbd0p2 mountpoint
>>```

>### オーバーレイイメージを無効化
>使用後はイメージをアンマウントし前の手順を逆にする事が重要
つまりパーティションをアンマウントしnbdデバイスを切断する
>```
>umount mountpoint
>qemu-nbd -d /dev/nbd0
>```

### RAMディスク参考URL
https://pcvogel.sarakura.net/2021/01/27/32111

# 手順のメモ(おそらく、信用出来ない、頼りにならない)

## 大まかな流れ(申し訳程度)
･マスターRAWイメージ(ブロックデバイス直でも可)を用意
･クライアント台数分の差分(オーバレイストレージ)イメージ生成
･各イメージの仮想パーティションデバイスエントリ作成→マウント
･iSCSIブロックデバイスのロジカルユニット追加→公開
･iSCSIクライアント接続→切断時に報告(仕組み要検討←Python+Flask+ShellScriptで実装可能?)
･該当iSCSIドライブ公開停止→ロジカルユニット削除
･https://linux.die.net/man/8/tgtadm
･該当仮想パーティションアンマウント→nbdデバイス切断
･該当イメージ再生成
(イニシエータ切断後に、差分イメージを再作成←RAMディスク化も要検討)
(オーバーレイストレージはqcow2形式←仮想デバイス化必要?)

## 具体的なコマンド(動くとは言っていない)
###  必要なパッケージをインストール
```
sudo apt install -y python3 python-is-python3
sudo apt install -y qemu-utils tgt
sudo pip3 install flask flask_httpauth ipaddress
```
###  iSCSI用のベースイメージを作成
```
sudo mkdir /var/lib/image
sudo dd if=/dev/zero of=/var/lib/image/base.raw count=0 bs=1 seek=250G
```
###  ベースイメージでiSCSI起動
```
sudo tgtadm --lld iscsi --op new --mode target --tid 1 -T iqn.2023-02.com.base:base
sudo tgtadm --lld iscsi --op new --mode logicalunit --tid 1 --lun 1 -b /var/lib/image/base.raw
sudo tgtadm --lld iscsi --op bind --mode target --tid 1 -I ALL
```
### ベースイメージ用iSCSIアカウント作成
```
sudo tgtadm --lld iscsi --mode account --op new --user iqn.2023-02.com.base:base --password basepasswd
sudo tgtadm --lld iscsi --mode account --op bind --tid 19 --user iqn.2023-02.com.base:base
```
### iscsiステータス表示
```
sudo tgtadm --lld iscsi --op show --mode target
```
### iSCSIターゲット停止
```
sudo tgtadm --lld iscsi --op unbind --mode target --tid 19 -I ALL
sudo tgtadm --lld iscsi --op delete --mode logicalunit --tid 19 --lun 19
sudo tgtadm --lld iscsi --op delete --mode target --tid 19
```
### ベースイメージ用iscsiアカウント削除
```
sudo tgtadm --lld iscsi --mode account --op delete --user iqn.2023-02.com.base:base
sudo tgtadm --lld iscsi --mode account --op unbind --tid 19 --user iqn.2023-02.com.base:base
```
### オーバーレイ1デバイス作成
```
sudo qemu-img create -o backing_file=/var/lib/image/base.raw,backing_fmt=raw -f qcow2 /var/lib/image/over1.qcow2
sudo modprobe nbd max_part=8
sudo qemu-nbd -c /dev/nbd1 /var/lib/image/over1.qcow2
sudo partprobe /dev/nbd1
#sudo mkdir /media/nbd1
#sudo mount /dev/nbd1 /media/nbd1
```
### オーバーレイ1 iSCSI 起動
```
sudo tgtadm --lld iscsi --op new --mode target --tid 1 -T iqn.2023-02.com.over1:overlay
sudo tgtadm --lld iscsi --op new --mode logicalunit --tid 1 --lun 1 -b /dev/nbd1
sudo tgtadm --lld iscsi --op bind --mode target --tid 1 -I ALL
```
### オーバーレイ1 iSCSIアカウント作成
```
sudo tgtadm --lld iscsi --mode account --op new --user iqn.2023-02.com.over1:overlay --password overlay1passwd
sudo tgtadm --lld iscsi --mode account --op bind --tid 1 --user iqn.2023-02.com.over1:overlay
```
### iSCSIステータス表示(再度確認)
```
sudo tgtadm --lld iscsi --op show --mode target
```
### オーバーレイ1 iSCSI停止
```
sudo tgtadm --lld iscsi --op unbind --mode target --tid 1 -I ALL
sudo tgtadm --lld iscsi --op delete --mode logicalunit --tid 1 --lun 1
sudo tgtadm --lld iscsi --op delete --mode target --tid 1
```
### オーバーレイ1 iSCSI アカウント削除
```
sudo tgtadm --lld iscsi --mode account --op delete --user iqn.2023-02.com.over1:overlay
sudo tgtadm --lld iscsi --mode account --op unbind --tid 1 --user iqn.2023-02.com.over1:overlay
sudo qemu-img create -o backing_file=/var/lib/image/base.raw,backing_fmt=raw -f qcow2 /var/lib/image/over1.qcow2
```
### iSCSIターゲットを再起動
```
sudo systemctl restart tgt.service
```

## WindowsのコマンドプロンプトからiSCSIドライブをアンマウント
https://seioe0world.hatenablog.com/entry/2017/09/19/023622
https://learn.microsoft.com/ja-jp/windows-server/administration/windows-commands/mountvol
https://qualitestgroup.com/insights/technical-hub/how-to-offlineonline-a-disk-using-diskpart/
```
mountvol i: /p
echo select disk 3 > temp.txt
echo offline disk >> temp.txt
diskpart -s temp.txt
powershell -Command "Disconnect-IscsiTarget -Confirm:$false"
set dir=path/to
powershell -Command "ls %dir%"
```
コマンドプロンプトからPowerShellを呼び出している
何かもっとスマートな方法があるはず

# 追記(2025/5/17)
Python+Flask(IP+Digest認証?)+Shell script(SUIDでRoot実行)をWebGUIやショートカットファイルでURLを叩く事で自動化しようとしていた
テスト環境(NVMe SSD+1Gb/s NIC) にて、WindowsからCrystalDiskMarkで書き込み速度のベンチマークを測定した所、使用したネットワークの実測値近くまで速度が出ていた為、概念実証レベルとしては大成功だった
![iscsi.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3884308/e51e90de-0af2-4057-8c87-f4acaaa6b3b4.png)
10Gb/s NICとNVMe SSDのRaid1環境で遊んだら楽しそうですね
(10ギガなスイッチングハブはまだまだ高価で手が出せないけれどね)
iSCSIターゲットがUbuntu 22.04 Server、iSCSIイニシエータがWindows10の環境で動かしていましたが、再現性は保証出来ません
作業中に走り書きしたメモを、思い出しながら申し訳程度に再編集した文章ですので、あくまで参考程度でお願い致します