k0s環境下におけるNFSのPV設定とPostgreSQLの導入

# はじめに

[前回](https://qiita.com/RCA3610/items/33c55f4444e81fca439c)に続けて、kubernetesの基本機能であるPVの構築、Podの生成の確認を実施したため備忘録として記載。
今回メインとして実施する内容は下記の通り。
* NFSをPVとして設定
* PostgreSQLのPodを起動

純粋なk8sで導入していれば気にしなくていい部分でもk0s経由だと支障をきたす部分が出たり、NFSをPVとして利用するときに注意する点があったので参考になれば嬉しいです。


# 構築手順
## k0s投入コンフィグ

下記のコンフィグを`k0sctl apply --config k0sctl.yaml`で投入、k8s環境を構築した。
今回からcalicoを導入、他のパラメータはデフォルトのものを利用している。（Pod間通信にCalicoを使ってみたかったため。今回の内容には直接の関連はなし）

```yaml:k0sctl.yaml
apiVersion: k0sctl.k0sproject.io/v1beta1
kind: Cluster
metadata:
  name: k0s-cluster
spec:
  hosts:
  - ssh:
      address: masternode
      user: root
      port: 22
      keyPath: ~/.ssh/id_rsa
    role: controller+worker
  - ssh:
      address: workernode01
      user: root
      port: 22
      keyPath: ~/.ssh/id_rsa
    role: worker
  k0s:
    version: v1.28.4+k0s.0
    dynamicConfig: false
    config:
      apiVersion: k0s.k0sproject.io/v1beta1
      kind: Cluster
      metadata:
        name: k0s
      spec:
        images:
          calico:
            cni:
              image: calico/cni
              version: v3.26.4
        api:
          k0sApiPort: 9443
          port: 6443
        installConfig:
          users:
            etcdUser: etcd
            kineUser: kube-apiserver
            konnectivityUser: konnectivity-server
            kubeAPIserverUser: kube-apiserver
            kubeSchedulerUser: kube-scheduler
        konnectivity:
          adminPort: 8133
          agentPort: 8132
        network:
          kubeProxy:
            disabled: false
            mode: iptables
          kuberouter:
            autoMTU: true
            mtu: 0
            peerRouterASNs: ""
            peerRouterIPs: ""
          podCIDR: 10.244.0.0/16
          provider: calico
          serviceCIDR: 10.96.0.0/12
        podSecurityPolicy:
          defaultPolicy: 00-k0s-privileged
        storage:
          type: etcd
        telemetry:
          enabled: true
```

構築完了後、masternodeにてPodの起動状態を確認、設定どおりに動いていることを確認した。

```console
# k0s kubectl get pod -A
NAMESPACE     NAME                                       READY   STATUS    RESTARTS   AGE
kube-system   calico-kube-controllers-84c6cd5b85-hflvt   1/1     Running   0          86s
kube-system   calico-node-8dtvw                          1/1     Running   0          81s
kube-system   calico-node-ctznc                          0/1     Running   0          78s
kube-system   coredns-85df575cdb-mr5wv                   1/1     Running   0          69s
kube-system   coredns-85df575cdb-wpdj6                   1/1     Running   0          86s
kube-system   konnectivity-agent-cmtnl                   1/1     Running   0          81s
kube-system   konnectivity-agent-jj4np                   1/1     Running   0          78s
kube-system   kube-proxy-2b7hx                           1/1     Running   0          78s
kube-system   kube-proxy-6msld                           1/1     Running   0          81s
kube-system   metrics-server-7556957bb7-8ddf8            1/1     Running   0          86s
```

## NFS構築

NFSサーバを立てるノードに下記コマンドを実行する。今回はbuildserver（k0sctlでk8s環境構築の指示を飛ばしているサーバ）に構築した。
なお、`/etc/exports`の設定内容についてはセキュリティ事項含めて各自の環境で決めること。
なお、`no_root_squash`オプションをつけないと権限エラーでPod生成が上手くいかない可能性があるため注意（PostgreSQLのPodがデータを保存する際、chownやchmodを伴う操作があり、これが実行できないとPostgreSQL PodがCrashLoopBackOffになるかError状態となる）

```console
# apt install nfs-kernel-server
# vim /etc/exports
（省略）
/home/user/nfs  *(rw,sync,no_subtree_check,fsid=0,insecure,no_root_squash)
# mkdir /home/user/nfs
# mkdir /home/user/nfs/pv001
# chmod 755 /home/user/nfs
# chmod 755 /home/user/nfs/pv001
```

各ノードで下記のコマンドでNFSクライアントプログラムをインストール
```console
# apt install nfs-common
```

上記の設定が完了した段階で適当なノードからNFSをマウント、書き込み権限などが適切か確認し無事動作すればNFS構築は完了

```console
# mkdir /mnt/nfs
# mount -t nfs 192.168.0.10:/pv001 /mnt/nfs
# cd /mnt/nfs
# touch sample.txt
# echo "TEST" > sample.txt
# cat sample.txt
```

## NFS接続用CSIDriverの導入

[ここ](https://github.com/kubernetes-csi/csi-driver-nfs/blob/master/docs/install-csi-driver-master.md)を参考に、masternodeでNFSのCSIDriverをインストールする。

```console
# apt install git
# git clone https://github.com/kubernetes-csi/csi-driver-nfs.git
# cd csi-driver-nfs
# ./deploy/install-driver.sh master local
```

k0sで構築した場合、初期設定のまま進めるとkubectlコマンドは`k0s kubectl`の形式で実行する関係上、スクリプトが上手く動かない可能性がある。
上記実行中にエラーが出力される場合は、下記ファイル内の`kubectl`のディレクトリの記述を`k0s/kubectl`に変更する。
```console
deploy/install-driver.sh
deploy/rbac-csi-nfs.yaml
deploy/csi-nfs-driverinfo.yaml
deploy/csi-nfs-controller.yaml
deploy/csi-nfs-node.yaml
```

例として、`deploy/csi-nfs-controller.yaml`の修正例を下記に記載する。
```diff_yaml:csi-nfs-controller.yaml
（省略）
          volumeMounts:
            - name: pods-mount-dir
-             mountPath: /var/lib/kubelet/pods
+             mountPath: /var/lib/k0s/kubelet/pods
              mountPropagation: "Bidirectional"
            - mountPath: /csi
              name: socket-dir
          resources:
            limits:
              memory: 200Mi
            requests:
              cpu: 10m
              memory: 20Mi
      volumes:
        - name: pods-mount-dir
          hostPath:
-           path: /var/lib/kubelet/pods
+           path: /var/lib/k0s/kubelet/pods
            type: Directory
        - name: socket-dir
          emptyDir: {}
```

無事軌道に成功すれば下記のようにPodが立ち上がる。
```console
# kubectl get pod -A
NAMESPACE     NAME                                       READY   STATUS    RESTARTS       AGE
（省略）
kube-system   csi-nfs-controller-5c4f6db55b-j695p        4/4     Running   0              15m
kube-system   csi-nfs-node-7r6f4                         3/3     Running   0              15m
kube-system   csi-nfs-node-gv4x4                         3/3     Running   0              15m
```

## NFSをPVで作成

今回はPersistent Volume (PV)を作成して構築を進めた。

```yaml:nfs-pv.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: nfs-pv
spec:
  storageClassName: manual
  capacity:
    storage: 8Gi
  volumeMode: Filesystem
  accessModes:
    - ReadWriteMany
  persistentVolumeReclaimPolicy: Recycle
  nfs:
    server: 192.168.0.10
    path: "/pv001"
```

テスト用にPostgresqlを起動した。

```yaml:nfs-postgres.yaml
apiVersion: v1
kind: Secret
metadata:
  name: postgres-password
type: Opaque
data:
  password: XXXXXX==
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: postgres-pv-claim
spec:
  storageClassName: manual
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 5Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: postgres
spec:
  selector:
    matchLabels:
      app: postgres
  template:
    metadata:
      labels:
        app: postgres
    spec:
      containers:
      - name: postgres
        image: postgres:13-alpine
        env:
        - name: POSTGRES_PASSWORD
          valueFrom:
            secretKeyRef:
              name: postgres-password
              key: password
        - name: PGDATA
          value: /var/lib/postgresql/data/pgdata
        ports:
        - containerPort: 5432
        volumeMounts:
        - name: postgres-persistent-storage
          mountPath: /var/lib/postgresql/data
          readOnly: false
      volumes:
      - name: postgres-persistent-storage
        persistentVolumeClaim:
          claimName: postgres-pv-claim
---
apiVersion: v1
kind: Service
metadata:
  name: postgres
spec:
  selector:
    app: postgres
  ports:
  - port: 5432
    targetPort: 5432
```

下記コマンドで上記内容を登録
```console
# k0s kubectl apply -f nfs-pv.yaml -f nfs-postgres.yaml
```

## 構築完了

全工程完了後、下記の状態になっている。

```console
# kubectl get pv -A
NAME     CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                       STORAGECLASS   REASON   AGE
nfs-pv   8Gi        RWX            Recycle          Bound    default/postgres-pv-claim   manual                  172m

# kubectl get pod -A
NAMESPACE     NAME                                       READY   STATUS    RESTARTS        AGE
default       postgres-87654496c-cngfc                   1/1     Running   0               171m
kube-system   calico-kube-controllers-84c6cd5b85-pwpcz   1/1     Running   1 (3h10m ago)   3h20m
kube-system   calico-node-mnhq5                          1/1     Running   1 (3h11m ago)   3h20m
kube-system   calico-node-qtb4t                          0/1     Running   0               3h20m
kube-system   coredns-85df575cdb-c86pv                   1/1     Running   0               3h20m
kube-system   coredns-85df575cdb-m6q7k                   1/1     Running   1 (3h11m ago)   3h20m
kube-system   csi-nfs-controller-5c4f6db55b-j695p        4/4     Running   0               3h10m
kube-system   csi-nfs-node-7r6f4                         3/3     Running   0               3h10m
kube-system   csi-nfs-node-gv4x4                         3/3     Running   0               3h10m
kube-system   konnectivity-agent-7l8s5                   1/1     Running   1 (3h11m ago)   3h20m
kube-system   konnectivity-agent-ldrrd                   1/1     Running   0               3h20m
kube-system   kube-proxy-qc2mc                           1/1     Running   0               3h20m
kube-system   kube-proxy-wdt6d                           1/1     Running   1 (3h11m ago)   3h20m
kube-system   metrics-server-7556957bb7-dfzrb            1/1     Running   0               3h20m

# kubectl exec -it postgres-87654496c-cngfc -- sh
/ # ls
（省略）
/ # exit
```

# さいごに
主にNFS設定とPV割り当てに手間取ったものの、無事設定完了。
次は自作Pythonプログラムをコンテナに詰め込み並列起動、どのコンテナからでもPostgreSQLに書き込みができるか確認したいと思います。


# 参考文献
* [Raspberry PiでKubernetesクラスター用のNFSを作る](https://qiita.com/reireias/items/76a22b938f1d3f829f8d)
* [Install NFS CSI driver master version on a kubernetes cluster](https://github.com/kubernetes-csi/csi-driver-nfs/blob/master/docs/install-csi-driver-master.md)
* [k0s should move /var/lib/k0s/kubelet to /var/lib/kubelet](https://github.com/k0sproject/k0s/issues/1842)
* [chown on a mounted NFS partition gives "Operation not permitted"](https://serverfault.com/questions/212178/chown-on-a-mounted-nfs-partition-gives-operation-not-permitted)