概要
- Raspberry Pi 4 Model B + M.2 SSDでNFSサーバーを構築
- KubernetesのPersistent Volumeとして登録
背景
自宅内で複数端末から接続できる Jupyter 環境欲しいなあ。
→ 例によってラズパイk8sクラスター上に作ろう。
→ ラズパイk8sクラスターに永続ボリューム無いからデータ保存どうしよう...
→ NFS追加しよう!せっかくだから見栄え意識してこれもラズパイで構築しよう!
Before After
After
最下段のハブを上に移動し、空いたスペースにラズパイ + SSDを追加しました。
※ ラズパイk8sクラスター作成に関してはこちらで記事にしています。
そもそも何故ラズパイk8sに別途永続ボリュームが必要なのか?
端的に言うと、k8sのPodは揮発性だから。
Pod中に保存してもデプロイ等でPodが再作成されると消えます。
なので、Persistent Volumeという概念がk8sにはあります。
図にするとこんな感じです。(実際にはPVCをつかってPVをPodに割り当てますが割愛します)
Dockerと名前つきボリュームの関係をイメージするとわかりやすいかも。
ハードウェア
今回追加したハードウェアは以下になります。
- Raspberry Pi 4 Model B 8GB
- Geekworm X873
- M.2 SSD (2280) 128GB NVMe
ラズパイのストレージにはmicroSDを使うのが一般的ですが、NFSとして永続ストレージに使用するには適していません。
そのため、SSDをストレージに採用します。
普通の2.5インチSSDでもいいのですが、クラスターの積層ケースに積むことを考慮してM.2 SSDを採用しました。
構築
ここからはOS、NFS、PersistentVolumeの構築を順に紹介していきます。
OSイメージの書き込み
まずは X873 をラズパイではなく、作業マシンにUSB接続します。
その状態でRaspberry Pi Imager を使用してSSDにOSイメージを書き込みます。
Raspberry Pi Imagerはシンプルなので公式ドキュメントを読めばわかります。
OSはせっかくメモリ8GBモデルにしているので、64bit版にします。
まあ、1プロセスで3GB以上メモリを使う予定はないのですが。
(32bitだと1プロセスのメモリが3GBに制限される)
64bit版は2021/05/23時点ではRaspberry Pi Imagerから選択できないため、以下の公式ダウンロードページから取得し、解凍し、 Use custom
から指定して利用します。
あとは WRITE
ボタンを押せばOSイメージがSSDに書き込まれます。
OSイメージが書き込めたら、追加でファイルを設置してsshを起動する設定にします。
再度SSDをUSB接続しなおして、 boot/ssh
ファイルを生成します。
# 起動時にsshを有効にする設定
# パスは私の環境の例
$ touch /media/reireias/boot/ssh
OS初期設定
ラズパイにSSDを接続し、電源を入れます。
ルーター等からIPさえ特定できれば、ラズパイにディスプレイもキーボードも接続せずに初期ユーザー + 初期パスワードでssh接続できるはずです。
(我が家にmicroHDMI端子が無くてつながるディスプレイはなかった...)
ssh鍵の設置、パスワード変更、swapの無効化、ホスト名の設定等を行っていきます。
最後にパッケージを更新して再起動をかけます。
# authorized_keysの設定
$ scp ~/.ssh/id_rsa.pub pi@ip:/tmp/authorized_keys
$ ssh pi@ip
$ mkdir ~/.ssh
$ cp /tmp/authorized_keys ~/.ssh/authorized_keys
# 初期パスワードを変更
$ sudo passwd pi
# swapを無効化
$ sudo systemctl disable dphys-swapfile.service
# ホスト名を設定
# 今回は pikube01 ~ pikube03 という名前にしました
$ sudo vi /etc/hosts
$ sudo vi /etc/hostname
# 何らかの手段でIPを固定化しておくと便利でしょう
# 私はルーターの機能で固定DHCPを設定しています
# packageの更新
$ sudo apt update
$ sudo apt upgrade
# もろもろを反映させるために再起動
$ sudo reboot
あとはお好みでrsyslogやnode_exporter等設定していくのもいいでしょう。
私は新しいラズパイに毎回設定するのが面倒なのでAnsibleのスクリプトを用意して流しています。
NFSの設定
「ラズパイ NFS」等でググると山ほど記事がでてきます。
通常の説明はそちらに譲るとして、本記事ではせっかくなので私が使ったAnsibleでのスクリプトを紹介します。
これさえ用意しておけば、何度故障したって新規構築は楽ですね。
ホスト名を pikube04
にしているので、一部ファイル名もホスト名に合わせています。
$ tree
.
├── inventories
│ └── pikube04.yml
├── pikube04.yml
└── roles
└── nfs
├── files
│ └── exports
└── tasks
└── main.yml
---
raspberrypi:
hosts:
pikube04:
vars:
ansible_user: pi
ansible_ssh_private_key_file: ~/.ssh/id_rsa
ansible_become: yes
---
- hosts: raspberrypi
roles:
- nfs
# /etc/exports: the access control list for filesystems which may be exported
# to NFS clients. See exports(5).
#
# Example for NFSv2 and NFSv3:
# /srv/homes hostname1(rw,sync,no_subtree_check) hostname2(ro,sync,no_subtree_check)
#
# Example for NFSv4:
# /srv/nfs4 gss/krb5i(rw,sync,fsid=0,crossmnt,no_subtree_check)
# /srv/nfs4/homes gss/krb5i(rw,sync,no_subtree_check)
#
/opt 192.168.0.0/255.255.255.0(rw,sync,no_subtree_check,all_squash)
※利用する場合はLANのCIDRに応じて 192.168.0.0/255.255.255.0
を修正してください。
---
- name: install nfs-server
apt:
name: nfs-kernel-server
update_cache: yes
- name: create nfs directory
file:
path: /opt/nfs
owner: nobody
group: nogroup
mode: 0755
state: directory
- name: create nfs sub directory
file:
path: "/opt/nfs/{{ item }}"
owner: nobody
group: nogroup
mode: 0755
state: directory
with_sequence: start=1 end=10 format=pv%02d
- name: copy exports
copy:
src: exports
dest: /etc/exports
owner: root
group: root
mode: 0644
- name: enable nfs-server
systemd:
name: nfs-server
enabled: yes
state: started
daemon_reload: yes
NFS用のディレクトリ配下にPersistentVolume用のディレクトリを /opt/nfs/pv01
〜 /opt/nfs/pv10
まで作成しています。
以上でNFSの設定ができたはずなので、試しに作業マシンからマウントして確認してみます。
$ sudo mount 192.168.xx.xx:/opt/nfs/pv001 /mnt/nfs
# マウントできたら読み書きできるか適当に試す
Persistent Volumeの登録
マニュフェストを書いてk8sにPVを登録します。
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv01
spec:
capacity:
storage: 5Gi
volumeMode: Filesystem
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Recycle
storageClassName: slow
nfs:
server: 192.168.xx.xx
path: /opt/nfs/pv01
Persistent Volumeの利用
最終的にはJupyterのストレージとして以下のようにPodを構成しています。
Persistent Volume ClaimでPVを払い出してもらう形になります。
以下では自作のJupyterイメージ中でWORKDIRを /opt
に指定しているため、Podの /opt
にPVをマウントしています。
これでJupyter上で保存したファイルがNFSに保存されます。
apiVersion: apps/v1
kind: Deployment
metadata:
name: jupyter-deployment
spec:
selector:
matchLabels:
app: jupyter
replicas: 1
template:
metadata:
labels:
app: jupyter
spec:
containers:
- name: jupyter
image: reireias/jupyterlab:0.0.3
ports:
- containerPort: 8080
volumeMounts:
- name: opt
mountPath: /opt
volumes:
- name: opt
persistentVolumeClaim:
claimName: jupyter-pvc
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: jupyter-pvc
spec:
accessModes:
- ReadWriteOnce
volumeMode: Filesystem
resources:
requests:
storage: 5Gi
storageClassName: slow
---
apiVersion: v1
kind: Service
metadata:
name: jupyter-service
spec:
selector:
app: jupyter
ports:
- port: 80
targetPort: 8080
type: LoadBalancer
まとめ
お家k8sクラスターに永続ストレージが増えた!
今までmicroSDの耐久性を気にして保存できなかったデータも保存できるようになった!
参考