【備忘録】ファイルストレージに興味があったので勉強して、作ってみた話
概要
ファイルストレージに突発的に興味が湧いたので、勉強してみました。
誤りを含む場合がありますのでご了承ください。
この記事は大まかに2部構成になっています。
- ストレージについて下層から理解するコーナー
- 実際にファイルストレージとしてPCを動かすコーナー
皆さんのストレージ学習の一助になれたら幸いです。
ストレージについて下層から理解するコーナー
この節は、「ファイル」というものが成り立ち、共有されるまでの過程を再発明しながら「ファイルストレージ」についての理解を深めることを目的としています。
NANDフラッシュ 〜まずは1ビットを覚えたい〜
我々がPCを使う理由は主に、「計算が早い」「物覚えがいい」の2つに集約されると思います。
「物覚えがいい」を満たすためには、受け取った情報をしまっておいて、好きなときに変わらない状態で取り出せる必要があります。
我々は、「ファイル」の再発明をしようとしているわけですが、今のところたった1ビット、ONとOFFすら覚えていられる知能がありません。
これを解決する素子が存在します。

NANDフラッシュ です。
半導体を上手に使うことで、1ビットの記憶が可能です。
覚え方
制御ゲートに強く電圧をかけることによって電子が絶縁体を無理やり通り抜けて電化蓄積膜に蓄えられます。

逆に、シリコン基板に強く電圧をかけることでもし電子が蓄えられていたらそれは放出されます。

この作業によって1ビットを表現することが可能です。
思い出し方
さて、電化蓄積膜に電子が入っているかどうかでビットの表現が可能と言いましたが、どうやってそれを判定するのでしょうか。
試しに、制御ゲートに、電化が絶縁体を飛び越えたりはしないくらいの電圧をかけてみましょう。
シリコンは半導体ですから、電圧をかけられることで電流が流れやすくなったりします。
もし、電子が蓄えられていたら?
かかる電圧は電子に打ち消されて少し弱くなり、電流は何もないときより流れにくくなっているはずです。
もっと上手に設計してあげれば、電子があるときには電流が流れない。電子がないときには流れる。という状態を実現できます。
このようになるゲート電圧を読み出し電圧と言ったりするらしいです。
おまけの話1
NANDフラッシュは電圧によって無理やり電子に絶縁体を超えさせます。
その作業が毎回完璧にうまく行くわけでもなく、次第にヘタって正しく記憶することができなくなってきます。
つまり、NANDフラッシュには明確な 寿命 が存在します。
何らかの都合で読み書きが頻繁に起きるメモリのことを ホットスポット とよんだりしますが、ホットスポットになってしまったNANDフラッシュはすぐにヘタって、利用不可のフラグによって使われなくなってしまいます。
SSD 〜もっといっぱい覚えたい、覚え直したい〜
ものを覚えられるようになったのは大きな一歩ですが、1ビットごときでできることはほぼ無いと言っていいでしょう。
そこで、このNANDフラッシュをいっぱいまとめて、ビット列を覚えたりしたいです。
少し実用的にビットを覚えられるようになったら、編集したくなることもあるでしょう。
これらを実現するには、NANDフラッシュを統治するものが必要そうです。

それが、 SSD です。
SSDはNANDフラッシュを統治するためのコントローラとキャッシュを備えています。
本来NANDフラッシュは「上書き」という操作を持ちません。
なので、新しいことはたとえ上書き事項であったとしても新しい領域に保存します。使わなくなった部分は印をつけて覚えておきます。
そして、コントローラの手が空いたときに、それらをまとめて0にして集めておきます。これを ガベージコレクション というふうに呼びます。
おまけの話2
「SSDの容量は満タンにしないほうがいい」という話を効いたことはありませんか?
その言説は本当で、先述のガベージコレクションと密接に関わっています。
容量が逼迫した状態のSSDでは、ガベージコレクションが頻繁に行われます。
「おまけの話1」で述べたように、NANDフラッシュには明確に寿命がありますから、頻繁なガベージコレクションによる読み書きはNANDを酷使し、へたらせていきます。
こうなると、SSDの不具合が起こりやすくなります。
皆さんはできるだけ余裕を持ったストレージ運用を心がけましょう。
ストレージプール 〜もっとたくさん、間違えずに覚えたい〜
もし、本格的にたくさん記憶して、外部との共有もしたいと夢見るならば、SSDが1枚だけということはないでしょう。
もしかしたらHDDも併用するかもしれません。
たくさんあるということは、統治者がいなければなりません。
その統治者のことを ストレージプール と呼びます。
ストレージプールは、ただ複数のドライブをまとめるだけではなく、保存する記憶に対する保護機能も備えてくれます。贅沢ですね。
「記憶の保護と言っても、何をするの?カバーを固くしたりするの?」
という疑問を持つかもしれません。(ちなみに、SSDはHDDに比べて磁気や衝撃に強いことが売りの1つです)
この「記憶保護とは何を持って行われるか?」という抽象的な問題にある種の答えを導き出した人物がいます。
David A.Patterson, Garth A.Gibson and Randy H.Katzの3名の論文の内容が、現代の「ストレージプールにおける記憶保護」の軸となっています。
RAIDと呼ばれるこのシステムには、現代まで様々な改良がなされ、多くのバージョンが存在しますが、ここでは深く触れずに概要だけ述べます。
- パリティ
- 冗長性
- ミラーリング
によってどれか1つドライブがイカれても復旧を可能にするようなシステムが考案されています。
ボリューム 〜余裕が生まれると、気遣いが生まれる〜
さて、ストレージプールによってリスクを少なくした状態でかなり多くの記憶をできるようになりました。
しかし、ドライブ本体は複数あるので、ドライブ間をまたいだ保存などは一苦労です。
更に、ストレージプールの少しの変更などによって、読み出す側はパニックに陥ってしまいます。
せっかく強力になって、余裕が生まれてきたので、情報を読む側への配慮をしてみましょう。
それが ボリューム です。
これは論理的なもので、今までのような物理的実態を持ちません。
ドライブの記憶領域を丁寧にマッピングして、コンピュータ内で不自由なく操作できる「見せかけ」が、ボリュームです。

割付方法は複数存在しますが、ここでは割愛します。
一旦まとめ
さて、ここまでの内容をまとめると、以下のような構造になります。

この人まとまりだけで、案外何でも覚えられてみんなのニーズを満たせそうではありませんか?
ここまでの構成をひとまとめにして ブロックストレージ と呼びます。
これから話すファイルストレージも、いつか扱うオブジェクトストレージも、根底にはこのブロックストレージが存在します。
ちなみに、AWSではEBS、といった名前で提供されています。
今回はファイルについての回なので、ブロックストレージについての扱いかたは省略させてください。
ファイルシステム 〜ビットを 使う ために〜
さて、これまでの主題は「どれだけ多くのビットを、間違いなく覚えるか?」でした。
しかし、我々はビットの羅列を見ても、それが意味するところを理解できません。
そして実は、PCも、現段階のブロックストレージを覗いても、何も理解することができません。
なぜなら、今のビット列には「区切り」が存在しないからです。
ビット列に対して、特定の区画はどういう意味を持って保存されたのか。
これを管理して初めて、PCは記憶を有効活用できます。
そこで登場するのが ファイルシステム です。
ファイルシステムは、ボリュームに対して保存処理を行う前に、細切れにされる情報に対して「タグ」をつけます。
これによって分散的に保存された情報たちは、好きなタイミングで意味のある塊として再構築することが可能になります。
更に、ファイルシステムはファイルに対して メタデータ を振ります。
これによって権限や所有者といった情報まで含めての管理が可能になり、本格的にファイルとしての運用が可能になります。
ちなみに、これらのタグやメタデータの集まりを inode というふうに呼んだりします。

systemctlなどで用いられるハイパーリンクもこの辺をうまく使っていそう。
おまけ話3
ちなみに、ファイルシステムはOSによって異なるものが使われている場合が多く、Linuxではext4やxfs。WindowsではNTFSなどが運用されています。
ファイルシェア/ファイルエクスポートサービス 〜映えを共有〜
ファイルシステムまでは、皆さんのPC1台の中でも動いているものでした。
ここからは少し別視点の話です。ファイルをPCの外部と共有したいと思ったとき、どのように管理すればいいでしょうか。
そこで活躍するのが ファイルシェア/ファイルエクスポートサービス です。
これらは、ファイルの共有に特化したシステムで、ファイルストレージをファイルストレージとして運用する上で外せない要素です。
複数デバイスからアクセスされることを前提に、DB回で取り扱った「排他制御」などを実現しています。
LinuxではNFS、WindowsではSMBがよく使われます。
実際にファイルストレージとしてPCを動かすコーナー
ファイル共有のためのサーバーを立てて、手元のPCにつなげてみます。
環境
- Ubuntu24.04 : ファイルサーバー用
- Windows11 : 使う側のPC
方針の整理
ファイルサーバーを構築する、と言ってもそんなに複雑なことはありません(たぶん)。
実は、ほとんどのPCにおいて、ファイルサーバーの機構はデフォルトで備わっています。
つまり、我々がすることは単につなぎこみだけ、ということになります。
グローバルIPを持つUbuntuのファイルを手元のWindowsにつなぎ込んでみましょう。
先程、LinuxではNFS、WindowsではSMBがよく使われている、という話をしました。
では、OSをまたいだファイル共有はできないのか、というとそんなことはありません。
今回はUbuntu側に Samba というSMBとのやり取り用のシステムを常駐させることでファイル共有を実現してみます。
セキュリティ問題
さて、ここで問題になるのがセキュリティです。
厳密なところまでは詰めませんが、グローバルIPを持つサーバーのファイルを普通のインターネットで共有してたら危険です。
どこで盗聴されるか、改ざんされるか、わかったものではありません。
HTTPSのように万全な暗号化がなされているわけでもないです。
そこで、VPNを利用してセキュリティを担保します。
VPNとは
インターネットの普及+社会の大規模化によって、より広域に専用回線を引く必要が出てきました。
そこで専用性を広域で実現するために出てきたのがVPN(Virtual Private Network)です。
VPNは、独自回線と同等のセキュリティ担保をインターネット上で行うことを目的としていて、それが実現できれば手段は問いません。
セキュリティ担保の方法として主に公開鍵・秘密鍵を用いた認証・暗号化があります。
SSHによく似ていますね。
VPNの実現方法の一つとして IPsec というものがあります。現在のVPNの多くはこれによって実現されています。
でも、今は使わないので一旦スルー。
WireGuard
WireGuardとは、VPNを実現するシステムの一つです。
OSに依存していないので、あらゆるOS上で使うことができます。
仕組みは、現在主流のIPsecをスリムにしたもので、ベースとなるプロトコルはだいぶ違っています。
例えば、IPsecはTCPで通信を行いますが、WireGuardではUDPを使います。
WireGuardは、高速通信と手軽さを謳っているので、今回はこちらを使って素早くVPNを結んでいきます。
前提の共有
以降手元のPCを「クライアント」、外部のPC(ファイルストレージにするやつ)をサーバと呼びます。
WireGuardを導入
とりあえず、ペアリング直前の待機状態までサーバーをセットアップ。
まずは WireGuardを入れます。
sudo apt install wireguard
NICを作る
次に、ネットワークデバイスを作りましょう。
ネットワークと通信するためには、IPアドレスを持った(仮想)デバイスが必要だからです。
まずは現状のデバイスを確認しておきます。
これはインフラを触る際に、もとの状態に戻せなくなるやつを防ぐためです。
ip a
で、現状のネットワークデバイスを確認しておきます。
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host noprefixroute
valid_lft forever preferred_lft forever
2: enX0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc fq_codel state UP group default qlen 1000
link/ether 06:cf:94:ef:f4:11 brd ff:ff:ff:ff:ff:ff
inet 192.0.2.1/20 metric 100 brd 172.31.47.255 scope global dynamic enX0
valid_lft 2740sec preferred_lft 2740sec
inet6 fe80::4cf:94ff:feef:f411/64 scope link
valid_lft forever preferred_lft forever
lo はループバック用のNICです。自分自身と通信するために使うものです。IPにループバック用の 127.0.0.1/8が割り当てられていますね。
enX0 は外部との通信を行うためのNICです。グローバルIPが割り当てられているのがわかります。
結果をどこかにメモしておいたりすると、手戻りがしやすそう。
さて、WireGuardでのVPNの通信用のNICを追加してみましょう。
sudo ip link add dev wg0 type wireguard
特にエラーなどでなかったら、再びNIC一覧を見てみます。
$ ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host noprefixroute
valid_lft forever preferred_lft forever
2: enX0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc fq_codel state UP group default qlen 1000
link/ether 06:cf:94:ef:f4:11 brd ff:ff:ff:ff:ff:ff
inet 192.0.2.1/20 metric 100 brd 172.31.47.255 scope global dynamic enX0
valid_lft 2740sec preferred_lft 2740sec
inet6 fe80::4cf:94ff:feef:f411/64 scope link
valid_lft forever preferred_lft forever
3: wg0: <POINTOPOINT,NOARP> mtu 1420 qdisc noop state DOWN group default qlen 1000
link/none
新しいデバイスが追加されたのが見て取れるかと思います。IPアドレスはまだ割り当てられていません。
IPを割り当てる
IPアドレスを割り当てます。VPNで使うだけなので、ローカルIPを割り当てます。
sudo ip address add dev wg0 192.168.2.1/24
NICの状態を確認してみます。
$ ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host noprefixroute
valid_lft forever preferred_lft forever
2: enX0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc fq_codel state UP group default qlen 1000
link/ether 06:cf:94:ef:f4:11 brd ff:ff:ff:ff:ff:ff
inet 192.0.2.1/20 metric 100 brd 172.31.47.255 scope global dynamic enX0
valid_lft 2401sec preferred_lft 2401sec
inet6 fe80::4cf:94ff:feef:f411/64 scope link
valid_lft forever preferred_lft forever
3: wg0: <POINTOPOINT,NOARP> mtu 1420 qdisc noop state DOWN group default qlen 1000
link/none
inet 192.168.2.1/24 scope global wg0
valid_lft forever preferred_lft forever
IPが振られているのがわかりますね。
秘密鍵・公開鍵の生成
暗号化に使うための鍵のペアを作成しまします。
公開鍵暗号方式は、送り手側が 相手の公開鍵 で暗号化して、受け手が 自身の秘密鍵 で復号します。
双方向のやり取りをするので、お互いが公開鍵・秘密鍵のペアを持っている必要があります。
さっそく、サ―バーの鍵を作りましょう。
Ubuntuについて、新規ファイルの権限はデフォルトで644とかだったりします。
しかし、秘密鍵を権限なしで見ることができる状態が生まれるのは良くありません。(実際、SSHの秘密鍵は権限が緩いと使えないようになっていたりします。)
そこで、鍵を作った瞬間から権限を600とかに絞っておくと安全。 umaskコマンドを使うと、権限から指定した権限を引く形で権限設定できます。(作成された瞬間に、です)
umask 077
では、秘密鍵を作成します。
wg genkey > private.key
WireGuardでは、使用する暗号方式も決まっているので、wgで提供される鍵生成コマンドを使います。
作られたファイルは作られた瞬間に権限600になっています。
次に、公開鍵を作ります。公開鍵は秘密鍵を元に作れます。
wg pubkey < private.key > public.key
NICをセットアップ
先ほど作成したNICに対して、鍵やポートなどの情報をセットします。
コマンドラインでひとつづつセットすることも可能ですが、ファイルに書いて一気に適用することもできます。
記録に残るのもうれしいので、今回はこちらの方式を採ります。
まずエディタでファイルを作成して……
sudo vim /etc/wireguard/myconf.conf
中に、現在までで設定できるサーバー側の情報を書きます。
[Interface]
PrivateKey = [private.keyの中身]
ListenPort = 51820
試しに、ファイルの設定を丸ごと適用してみます。
sudo wg setconf wg0 /etc/wireguard/myconfig.conf
NICの設定状況は
sudo wg show
でできます。
出力例)
interface: wg0
public key: tnq4h+MaDwoxb1rAoH4iFlTn8jjnf2m6Wl6fI2it9SA=
private key: (hidden)
listening port: 51820
相手はまだいません。
クライアント側のセットアップ
Windows側にもWireGuardを導入。
インストールが終わったら、「からのトンネルを作成」を選択。
それによって、今サーバー側で行ったようなこと(鍵生成やNIC生成)が自動で行れるようです。
「編集」を押下してペア設定をします。
こちら側には、サーバーの情報を伝えることができます。
以下のように編集。
[Interface]
PrivateKey = [元から埋まっている秘密鍵]
Address = 192.168.2.2/24
[Peer]
PublicKey = [サーバーの公開鍵]
AllowedIPs = 192.168.2.1/32
Endpoint = [サーバーのグローバルIP]:51820
PersistentKeepalive = 25
前提でお話した通りのIPを割り当てるように設定しています。
書けたら、有効化。
サーバーにクライアントを伝える
クライアントをセットアップできたので、その情報をサーバー側に伝えます。
設定ファイルに書いて適用するという方法は変わりません。
[Interface]
PrivateKey = [サーバー秘密鍵]
ListenPort = 51820
[Peer]
PublicKey = [クライアント公開鍵]
AllowedIPs = 192.168.2.2/32
先ほど同様に、ファイルを元に設定をセットします。
sudo wg setconf wg0 /etc/wireguard/myconfig.conf
確認も忘れずに
sudo wg show
ペア設定が追加されていることを見られればOK。
NIC起動!!
サーバー側のNICは作られているもののまだ起動していません。
起動します。
ip link set up dev wg0
起動出来たら、ペアリングができているか
sudo wg show
で確認。
Sambaを導入する
VPNがつながったので、ファイルストレージを作ることができます。
ファイルサーバーにしたいUbuntu側にSambaを導入しましょう。
sudo apt install -y samba
バックアップを取る
普段のアプリ開発ではGitHubなどのログシステムを使って、簡単にやり直しなどを行うことができます。
mkdir /home/[ユーザー名]/backup
sudo cp -a /etc/samba/smb.conf /home/[ユーザー名]/backup/smb.conf.default
共有設定
次に、設定ファイルに変更を加えます。末尾に
[share]
path = /home/[ユーザー名]
browsable = yes
writable = yes
guest ok = no
read only = no
valid users = [ユーザー名]
を追記。
これによって、ユーザーのホームディレクトリを/shareで共有できるようになります。
validate users は、ファイル共有のためのユーザーで、サーバーのユーザとは関係がありません。
ユーザー追加
Sambaにユーザーを追加。
sudo smbpasswd -a [ユーザー名]
これによって、ユーザーにパスワード設定を行えます。
Windowsのファイルエクスプローラの上のパス入力窓に
\\192.168.2.1\share
と打ち込むと、VPNを通してファイルを共有することができます。
更に、その共有ファイルを起きたい場所で右クリックして、
「リモートの場所を追加」をすることによってエクスプローラにつなげることが可能です。
まとめ
本記事では、「ファイル」の保存の仕組みをひとさらいして、最終的にファイルサーバーを構築するところまでやってみました。
皆さんのストレージの理解や、新しいものづくりのモチベーション向上の一助になれたなら幸いです。
参考文献
[1] 坂下幸徳 基礎からの新しいストレージ入門 ~基本技術から設計・運用管理の実践まで~ 2023
Discussion