Debian GNU/Linux上でのシェルスクリプト実行によるNAS制御
長年、継続利用しているASUSTOR NASの気に入っているところは下記の2点。
- NASを利用しないときはスリープモード機能を設定することで、指定された時間内にデータアクセスがない場合は、システムをサスペンドさせることができる。例えば、30分間、NASへのアクセスがなければサスペンドにするような運用ができる。
- 数種類のバックアップ機能があるが、外部バックアップ機能を利用すると、USB接続HDD(NTFS)にデータをバックアップできる。スケジューリングさせてもよいし都度でもよいし、タイミングは自由に設定できる。
NASといえど、家庭内利用において24時間稼働させる必要はなく、逆に24時間稼働だと電気料金もかかってしまう。ASUSTORのNASでは、PCを利用していてNASを利用したいときに電源オン、NAS利用が終わったら30分後に自動的に電源オフのような使い方ができる。
また、常にリアルタイムバックアップを行っていると、うっかりファイル削除してしまっときにリカバリができないし、すべてのデータがオンライン状態のときランサムウェアに感染してしまうと、データ復旧も絶望的になる。そのためオフラインのバックアップをいくつか持っておくことが非常に重要になっている。
これらの2つの要件をASUSTORのNASは満たしてくれるため、我が家では大変重要な働きをしている。今回、新しく構築したTrueNAS SCALE/NASでも上記2つの機能を持たせたく、TrueNAS SCALEのWebUIで設定メニューを確認してみた。なんとなくバックアップやシャットダウン制御などできそうなのだが、一筋縄ではいかなそう。
TrueNAS SCALEを調査し続ける案もあったのだが、自分のスキルでは時間がかかりそうな雰囲気。そこで、10年以上も前に構築した自作NASでもシェルスクリプト実行によりシャットダウン制御などを行っていたため、TrueNAS SCALEでも同じような方法で制御できないかと考えた。ベースがDebianのためほぼLinux状態で利用することが確認できたのだが、私が試した限りでは、/etc/vconsole.confでのキーボード設定や/etc/fstabでのマウント設定などが反映されない(設定してもリブートの都度、初期化されてしまう)。少々不思議なところがあるし、少し工夫しないといけない部分もあるが、実装したい機能が実現できることが分かった。
※その結果、H342の改造に着手したというのが実際のところ。
必要な事前確認と設定
TrueNAS SCALEのベースはDebianのため、普段linuxを触る時と同じように設定できるものと考えていた。下記の通り、OS自体はDebianそのもののように見える。
root@H342-New[~]# cat /etc/os-release
PRETTY_NAME="Debian GNU/Linux 11 (bullseye)"
NAME="Debian GNU/Linux"
VERSION_ID="11" VERSION="11 (bullseye)"
VERSION_CODENAME=bullseye
ID=debian
HOME_URL="https://www.debian.org/"
SUPPORT_URL="https://www.debian.org/support"
BUG_REPORT_URL="https://bugs.debian.org/"
いざ、いろいろと設定変更していこうとしたところ、まず日本語キーボードの問題に直面、コンソールでのキーボード設定がリブートの都度初期化されてしまうのだ。この問題は、ある程度セットアップが完了したためHDMI接続の必要性がなくなり、途中からRLoginを使ってrootによるリモートログインを許容したことで無理やり解決状態にした(rootでのリモートログインは本来NGなのだが、外部からの接続は完全遮断のため)。
root@H342-New[~]# cat /etc/vconsole.conf
# CONSOLE CONFIGURATION FILE
KEYMAP="jp" <== これが有効にならない
なお、参考までにTrueNAS SCALEのSystem Settingsの中に”シェル”というメニューがあるのだが、TrueNAS SCALEとしての”admin”ユーザであり、Linuxのrootユーザではない(プロンプトが “$”)。visudoなどをいじってsudoやsu – ができるように試みたのだが、何をやってもうまくいかなかった。そもそもの考え方ややり方が間違っているのかもしれない。
別の問題はリブートするとWake on LAN機能が無効化されてしまう点だ。ethtoolでwolを有効化設定しても、次回起動時にwolが有効になるだけで、それ以降は再び無効化されてしまう。設定変更後の1回目だけがwolを利用可能な状態なのだ。これは、cronでリブートの都度、ethtoolコマンドを実行させることで解決させた。
root@H342-New[~]# crontab -l
@reboot /usr/sbin/ethtool -s enp1s0 wol g <== リブートの都度、ethtoolを実行
root@H342-New[~]# ethtool enp1s0 | grep -i wake
Supports Wake-on: pumbg
Wake-on: g <== g:wol有効、d:wol無効
また、/etc/fstabの設定を変更してもリブートの都度強制的に初期化されてしまい/etc/fstabへの追加設定が消去されてしまう。かつ、USB接続HDDのデバイス名(/dev/***の部分)が起動ごとに変わってしまうため、永続的なマウントポイントを設定できない。UUIDをfstabに記述したとしても初期化されて設定が削除されてしまうので意味がない。USB接続HDDを利用するたびに、毎回システムにログインしてマウンドコマンドを実行するのも面倒なので、これは後述のリモート制御でカバーする。
root@H342-New[~]# cat /etc/fstab
boot-pool/grub /boot/grub zfs relatime,defaults 0 0
tmpfs /run/lock tmpfs rw,nosuid,nodev,noexec,relatime,size=100m 0 0
※ 何を追加しても、再起動の都度、上記の設定に初期化されてしまう
NAS構成
ASUSTORとTrueNAS SCALEの2台構成となったが、USB接続HDDへのバックアップは以下のような設計で運用している。先述の通りASUSTORのNASで実現していたことを、TrueNAS SCALEにも実装した。ただ、ASUSTOR/NASとTrueNAS SCALE/NAS間での連携は取れていないため、手動でメイン、バックアップを使い分けている。
TrueNAS SCALE/NASのリモート制御だが、起動はWake on LANツールで問題なく対応できる。シャットダウンやバックアップなどを毎回リモートログインしてコントロールするのも面倒だし、自分以外の家族がNASを利用するようになったとしても家電製品の電源をオン・オフするような感覚で簡単に利用できないと使い勝手が悪くなってしまう。そこで、ずっと以前にも利用していた「特定ディレクトリでのファイル存在によって、動作を制御する」というやり方を今回も踏襲した。
下記は、毎分で定期的にファイル存在を確認するcron設定。毎分、cronでこれらを走らせてもマシン的には全く問題ない。
root@H342-New[~]# crontab -l
@reboot /usr/sbin/ethtool -s enp1s0 wol g <== これは先ほどのwol有効化の設定
*/1 * * * * /root/poweroff.sh <== NASのシャットダウン
*/1 * * * * /root/mount-usbhdd.sh <== USB接続HDDのマウント
*/1 * * * * /root/usbhdd-backup.sh <== USB接続HDDへのバックアップ
NASのシャットダウン
PCもシャットダウンしてNASも利用しないというときは、NASの電源をオフにしたい。毎回、NASの管理者画面にログインしなくてもシャットダウンできるようにする。
シャットダウンコマンドを実行させるシェルスクリプトは以下の通り。
root@H342-New[~]# cat poweroff.sh
#!/bin/sh
#H342 shutdown script from remote PC
if
test -e /mnt/Pooh-san-20230325/control/shutdown/* <== ディレクトリ内のファイル有無を確認
then
rm -rf /mnt/Pooh-san-20230325/control/shutdown/* <== ファイルがあれば、それを削除して、
/bin/systemctl poweroff <== NASをシャットダウン
else
echo > /dev/null
fi
非常に簡単なシェルスクリプトではあるが、ファイル格納してから最長でも1分後には問題なくNASシステムをシャットダウンできる。わざわざシステムに管理者権限でログイン –> シャットダウンの手順をとならくてもよい。
USB接続HDDのマウント
ブート時にUSB接続のHDDが接続されていれば問題ないのだが、普段はオフラインにしているためNASを利用している途中で「USB接続HDDにバックアップしておきたい」ときがある。こうなると、システムにログインしてマウントコマンドを打たなければUSB接続HDDを利用できない。では、毎回これをやるのかというと面倒なため、Windowsからの共有フォルダ操作一つでマウントができるようにした。
マウントコマンドを実行させるシェルスクリプトは以下の通り。
root@H342-New[~]# cat mount-usbhdd.sh
#!/bin/sh
#H342 USB-HDD mount script from remote PC
if
test -e /mnt/Pooh-san-20230325/control/mount/* <== ディレクトリ内のファイル有無を確認
then
rm -rf /mnt/Pooh-san-20230325/control/mount/* <== ファイルがあれば、それを削除して、
mount --types ntfs UUID=3E8A23848A2337B3 /mnt/usb8 <== USB接続HDDをNTFSでマウント
else
echo > /dev/null
fi
USB接続HDDをマウントするとき、/dev/***形式のデバイス名が利用できない。ブートの都度、/dev/sdg2だったり/def/sdf2だったりで毎回デバイス名が変わってしまう。そのため、UUIDを使ってマウントしている。下記はシステムで利用しているストレージデバイスのUUIDを確認したもの。
root@H342-New[~]# ls -l /dev/disk/by-uuid
total 0
lrwxrwxrwx 1 root root 10 May 5 19:44 10109663148903458986 -> ../../sde2
lrwxrwxrwx 1 root root 10 May 5 19:43 14286568303848588196 -> ../../sda3
lrwxrwxrwx 1 root root 10 May 5 19:44 1c8a83da-205a-4a08-8a4e-1d56e84b2c7d -> ../../dm-1
lrwxrwxrwx 1 root root 10 May 5 19:44 2cb14b69-0552-4f5d-ad5a-4b45071cbb0a -> ../../dm-0
lrwxrwxrwx 1 root root 10 May 5 19:43 3E8A23848A2337B3 -> ../../sdf2 <== USB接続HDD
lrwxrwxrwx 1 root root 10 May 5 19:43 D9E8-55A4 -> ../../sda2
上記のタイミングでは、3E8A・・・のUSB接続HDDは/dev/sdf2のデバイス名になっているようだ。なお、このHDDをフォーマットするとUUIDが変わってしまうため、再度設定をやり直す必要がある。
USB接続HDDへバックアップ
普段はオフラインにしているUSB接続HDDはとても重要な役割を果たしている。NASはraid-zで耐障害性を確保しているが、ファイルを削除すると当たり前だがNASのファイルシステムからは削除されてしまう。raidはバックアップソリューションにはならない。また、万が一、自宅内のPCがランサムウェアに感染しNASに格納されているファイルが影響を受けたとしてもオフラインでバックアップを持っておけば、問題なく復旧ができる。今、我が家には少なくとも2台のオフラインバックアップが存在している。もちろん、家族へのセキュリティに対する啓蒙は頻繁に行っている。
この外部バックアップも共有フォルダへのファイル投げ込み方式で実現している。
バックアップコマンドを実行させるシェルスクリプトは以下の通り。マウント手段を先ほどのとおり個別に作りこんだが、個別にマウント –> バックアップの手順を取らなくても、マウントしてバックアップまでを一気にできるようにしている。特徴はローカルバックアップなのにrsyncを利用している点。本来rsyncはリモートホストとのディレクトリ同期に利用するコマンドだと思うのだが、相手をローカルディスクに設定すればUSB接続HDDのバックアップに使えると考えた(普通のことなのかもしれないが)。バックアップに時間がかかるケースも想定して、「今日はPCをもう利用しないな」というタイミングで実行できるように、バックアップ後にシャットダウンさせている。
root@H342-New[~]# cat usbhdd-backup.sh
#!/bin/sh
#H342 backup --> shutdown script from remote PC
mount --types ntfs UUID=3E8A23848A2337B3 /mnt/usb8 <== NAS起動後に外付けHDDを接続してもOK
if
test -e /mnt/Pooh-san-20230325/control/backup/* <== ディレクトリ内のファイル有無を確認
then
rm -rf /mnt/Pooh-san-20230325/control/backup/* <== ファイルがあれば、それを削除して、
rsync -ah --exclude '/mnt/Pooh-san-20230325/ix-applications' --log-file=/var/log/rsync/`date "+%Y%m%d-%H%M%S"`.log --delete --backup --backup-dir="/mnt/usb8/backup-$(date +%Y%m%d-%H%M%S)" /mnt/Pooh-san-20230325/ /mnt/usb8/backup-latest/ --stats > /home/admin/usbbklog-$(date +%Y%m%d-%H%M%S)
<== USB接続HDDへバックアップ
wait <== バックアップが完了するまで待つ
/bin/systemctl poweroff <== バックアップ完了後、シャットダウン
else
echo > /dev/null
fi
rsyncでのバックアップはオプションを使って以下のように運用している。
- TrueNAS SCALEのストレージプールには、”ix-applications”などシステムが利用するディレクトリが存在していることから、これをバックアップ対象外にする。同様に”control”ディレクトリもバックアップ対象外とする(–exclude)。
- NASの共有フォルダから削除したファイルがいつまでも外付けHDDに残らないように、NASで削除したファイルは外付けHDDのバックアップ領域からも削除する(–delete)。
- かといって、すぐに外付けHDDから完全削除してしまうと「残っていれば復活できたのに」と思うことがあるかもしれないため、外付けHDDのバックアップ領域とは異なるディレクトリに一時的に退避させる(–backup)。ま、後から手動で削除することにはなるのだけれど。
rsyncのログ出力設定はもう少し見直さないといけないのだが、rootでログインした状態ではバックアップされた全てのファイルリストとその結果がわかるように出力し、adminでログインした状態では結果だけがわかるように設定している。ほぼ、同じ出力になるのだが・・・。
以下はrsyncの–log-file設定で出力したもの。
root@H342-New[/var/log/rsync]# cat 20230501-183301.log
・・・ snip ・・・ <== ここに膨大なファイルのリストが表示される
2023/05/01 18:38:13 [15503] Number of files: 324,906 (reg: 298,363, dir: 14,181, link: 12,356, dev: 5, special: 1)
2023/05/01 18:38:13 [15503] Number of created files: 22,509 (reg: 12,527, dir: 3,021, link: 6,957, dev: 4)
2023/05/01 18:38:13 [15503] Number of deleted files: 46,562 (reg: 27,194, dir: 5,522, link: 13,846)
2023/05/01 18:38:13 [15503] Number of regular files transferred: 12,574
2023/05/01 18:38:13 [15503] Total file size: 3.49T bytes
2023/05/01 18:38:13 [15503] Total transferred file size: 725.82M bytes
2023/05/01 18:38:13 [15503] Literal data: 725.83M bytes
2023/05/01 18:38:13 [15503] Matched data: 0 bytes
2023/05/01 18:38:13 [15503] File list size: 1.25M
2023/05/01 18:38:13 [15503] File list generation time: 0.001 seconds
2023/05/01 18:38:13 [15503] File list transfer time: 0.000 seconds
2023/05/01 18:38:13 [15503] Total bytes sent: 734.97M
2023/05/01 18:38:13 [15503] Total bytes received: 7.74M
2023/05/01 18:38:13 [15503] sent 734.97M bytes received 7.74M bytes 2.38M bytes/sec
2023/05/01 18:38:13 [15503] total size is 3.49T speedup is 4,700.16
こちらは、rsyncの–statsオプションで出力したもの。
root@H342-New[/home/admin]# cat usbbklog-20230501-183301
Number of files: 324,906 (reg: 298,363, dir: 14,181, link: 12,356, dev: 5, special: 1)
Number of created files: 22,509 (reg: 12,527, dir: 3,021, link: 6,957, dev: 4)
Number of deleted files: 46,562 (reg: 27,194, dir: 5,522, link: 13,846)
Number of regular files transferred: 12,574
Total file size: 3.49T bytes
Total transferred file size: 725.82M bytes
Literal data: 725.83M bytes
Matched data: 0 bytes
File list size: 1.25M
File list generation time: 0.001 seconds
File list transfer time: 0.000 seconds
Total bytes sent: 734.97M
Total bytes received: 7.74M
sent 734.97M bytes received 7.74M bytes 2.38M bytes/sec
total size is 3.49T speedup is 4,700.16
異なるのはバックアップ対象のファイルリスト有無だけなので、近いうちにもっとスマートな出力となるように整理したい。
ちょっとの工夫でNASがより便利で快適に
以上がTrueNAS SCALEでの設定以外にOSレベルで行ったセットアップの内容となる。NASといえども一般家庭では24 by 7での運用は必要ないと思うので、利用したいときに起動させて利用が終わったら電源を切るという操作を簡単に実現できるとPC間隔での利用ができて便利だ。内蔵HDDの稼働時間もかなり短縮できて故障リスクも減らせるのかもしれない。起動時のスピンアップのほうが問題だという突っ込みがあるかもしれないけれど。
バックアップ機能も実現できたし当初の目的はある程度達成できたのだが、シャットダウンし忘れるといつまでも稼働を継続してしまうという問題が残っている。この点はASUSTORに負けている。家庭内のPCとのセッションが存在しなくなったらシャットダウンさせるなどの追加設定も盛り込む予定だ。
NASのシャットダウンを自動化(2023年7月更新)
人間がシャットダウンし忘れても問題ない!
control/shutdownフォルダを利用したシャットダウン制御により、NASの電源オフができるようにしているが、この操作をし忘れるとPCを利用しなくなったのにずっとNASだけは動作し続けてしまい、無駄な電気料金を発生させてしまう。ASUSTORのNASのようにリモートのPCの動作状況やバックアップ動作の状態に応じてNASの状態を判別し、自動でシャットダウンさせるためのシェルスクリプトを追加した。
シャットダウンさせるための条件として以下の2つを設定した。
- 条件判定1:NASがバックアップ動作中ではない。外付けUSB-HDDへのバックアップ動作中の場合は、シャットダウンさせてはいけない。
- 条件判定2:PCが動作していない。PC起動中はNASを利用することが多いため、シャットダウンさせてはいけない。(もちろん、PC起動=常時NASを利用ではないのだが)
この2つの条件を同時に満たすことができれば、NASが利用状態にはないと判断できるため、シャットダウンさせても問題ない。逆にどちらかの条件を1つでもクリアできなければ、シャットダウンできない状態にあると判断できる。言い換えると下記のようになる。
- NASがバックアップ中である = シャットダウンさせてはいけない
- PCが動作している = シャットダウンさせてはいけない
「シャットダウンさせる」ことが最終目的となるため、最初に記載したNOTの条件判定でスクリプトを作成した。ちょっと紛らわしい・・・。
NASがバックアップ中かどうかの確認
このNAS(H342改)では、外付けUSB-HDDへのバックアップをrsyncを利用して実現している。そのため、rsyncプロセスの起動状況を確認することで、バックアップ中なのか、バックアップ中ではないのかを判断することができる。
rsyncプロセスが動作していないときは、下記のようにrsyncプロセスが表示されない。
root@H342-New[~]# ps -ef | grep rsync
root 58239 55398 0 10:07 pts/0 00:00:00 grep rsync
rsyncプロセスが動作しているときは、下記のようにrsyncプロセスがいくつか表示される。ah以降のオプションは横に長くなるため、カットして掲載している。
root@H342-New[~]# ps -ef | grep rsync
root 58517 58497 26 10:08 ? 00:00:00 rsync -ah <== rsyncプロセスが起動中
root 58518 58517 0 10:08 ? 00:00:00 rsync -ah
root 58519 58518 17 10:08 ? 00:00:00 rsync -ah
root 59303 55398 0 10:08 pts/0 00:00:00 grep rsync
このrsyncプロセスの数をカウントすることで、バックアップ動作状態を判別する。もちろん、”grep rysnc” の行はカウント対象外とする。
PCを利用中かどうかの確認
NAS上でのsmbのセッション有無やその数でPCの利用状況を確認・判断できるかもしれないが、ping監視を行う方法が最も簡単と考え、複数PCに対するpingチェックをシェルスクリプト化することにした。
PCの動作状況によって下記のようにping応答が異なる。この例では、4オクテット目が100と102のPCからは応答がないが、101のPCからは応答がある。この表示から「PCが動作していない」という条件(テキスト)をピックアップしなければならないのだが、確実な判断ロジックとするため、今回は ” ttl ” がping結果に表示されていないときは、PCが動作していないという条件式とした。ping応答がなければ、” ttl ” 表示が出てこないという考えである。なお、”-c 2″ としてpingを2回実行しているのは、arp解決の関係で、1発目のpingをロストするケースを想定して2回ping実行としている。現在のネットワークではほぼ気にしなくてもよいかもしれないが。
root@H342-New[~]# ping 192.168.1.100 -c 2
PING 192.168.1.100 (192.168.1.100) 56(84) bytes of data.
From 192.168.1.20 icmp_seq=1 Destination Host Unreachable
From 192.168.1.20 icmp_seq=2 Destination Host Unreachable
--- 192.168.1.100 ping statistics ---
2 packets transmitted, 0 received, +2 errors, 100% packet loss, time 1006ms
pipe 2
root@H342-New[~]# ping 192.168.1.101 -c 2
PING 192.168.1.101 (192.168.1.101) 56(84) bytes of data.
64 bytes from 192.168.1.101: icmp_seq=1 ttl=128 time=0.671 ms <== ping応答あり(ttl表示)
64 bytes from 192.168.1.101: icmp_seq=2 ttl=128 time=1.45 ms
--- 192.168.1.101 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1012ms
rtt min/avg/max/mdev = 0.671/1.058/1.445/0.387 ms
root@H342-New[~]# ping 192.168.1.102 -c 2
PING 192.168.1.102 (192.168.1.102) 56(84) bytes of data.
From 192.168.1.20 icmp_seq=1 Destination Host Unreachable
From 192.168.1.20 icmp_seq=2 Destination Host Unreachable
--- 192.168.1.102 ping statistics ---
2 packets transmitted, 0 received, +2 errors, 100% packet loss, time 1015ms
pipe 2
NASの自動シャットダウンスクリプト
これらの条件をまとめると下図のようになる。
実際に作成したシェルスクリプトは以下の通り。動作確認したいPCを追加したい場合は、配列に要素(IPアドレス)を追加していけばよい。
#!/bin/bash
IP_LIST=("192.168.1.100" "192.168.1.101" "192.168.1.102") <== 動作確認したいPCのIPアドレスを配列設定
PS=rsync <== チェックしたいプロセスを変数PSへ代入
count=`ps -ef | grep $PS | grep -v grep | wc -l` <== "rysnc"が表示された行数を"count"へ代入
for IP in "${IP_LIST[@]}" <== 配列にあるIPアドレスを変数IPへ代入
do
result+="$(ping $IP -c 2)" <== 変数IPに対してping、結果を"result"へ追加
result+="\n" <== 改行で結果を区切る
done
if [ $count = 0 ] && [[ $result != *ttl* ]]; then <== rsyncカウントがゼロ、ping結果にttlがない場合
/bin/systemctl poweroff <== NASをシャットダウン
else
echo > /dev/null <== ifの条件に一致しない場合、何もしない
fi
シェルスクリプト内には、今回の目的に照らし合わせると不要な行や変数がいくつか見られるが、標準出力で動作確認しながら(echo表示等)作成したため、最終的にこのような形となった。
また、当初、pingの結果を格納する変数 “result” に “+” を追加するのが漏れており、何度テストをやっても配列の最後の要素の結果のみがresultとして表示される状況にあった。追加ではなく代入になってしまい、100と101の結果が残らなくなっていたのだ。これに気付くのにかなりの時間を要してしまって、なんとも間抜けな状態であーでもない、こーでもないとテストしていた。
最後にこのシェルスクリプトをcronに設定することで、毎分状態チェックを行い、必要に応じてNASを自動でシャットダウンしてくれる。
root@H342-New[~]# crontab -l
@reboot /usr/sbin/ethtool -s enp1s0 wol g <== wol有効化の設定
*/1 * * * * /root/poweroff.sh <== NASのシャットダウン
*/1 * * * * /root/mount-usbhdd.sh <== USB接続HDDのマウント
*/1 * * * * /root/usbhdd-backup.sh <== USB接続HDDへのバックアップ
*/1 * * * * /root/ping-test.sh <== NASの自動シャットダウン(今回追加)
もはや、手動でシャットダウンさせるための “poweroff.sh” は不要ともいえるのだが、残っていても特に問題はないため、このままの状態で運用を継続している。
自作NASの完成形!?
NASを利用したいときは、wake on LANツールを使って手動オペレーションが必要となるが、起動後はNASの動作状況やPCの利用状況に応じて自動でシャットダウンしてくれるので、ASUSTORのNASとほぼ同等の機能を実現できた。誰も使っていないのに一日中、NAS(H342改)が動きっぱなしという状態を回避できる。
スマートフォン対応やNAS専用アプリの追加などではかなわないところはあるものの、自作NASとしてはある程度完成形に到達できたかなと考えている。