KubernetesでNFS領域をhostPathでマウントしても良いんじゃないの？

KubernetesのhostPathは、Podが稼働するノードによってデータが見えたり見えなかったりするのがよろしくないとされることが多いのだが、じゃあ、NFSサーバーを用意してhostPathの領域をOSがNFSマウントしておけば全部解決じゃん？という発想に対してはKubernetesに詳しければ詳しいほど否定的に傾く傾向が強い。

というのを下のStack Overflowの記事を読んで思ったのだが、実際のところOSがマウントしたNFS領域をhostPathで使うのも別に悪くはない。

https://stackoverflow.com/questions/65568898/difference-between-nfs-pv-hostpath-pv-on-nfs-and-hostpath-mount-in-deployment

OpenShiftとかクラウドのマネージドクラスターによってはOSにNFS領域をマウントできないとか、オンプレだと高可用なNFSサーバーを建てるのが難しいというのもあるか。
それはそれとして、自作KubernetesのワーカーノードにAmazon EFSをマウントしてPodからhostPathで使えるのを確かめたのが以下の手順。まあ要するに個人的なメモ。

# Kubernetesクラスターを作る
以下の記事より、UbuntuのEC2にセルフインストールでKubernetesクラスターを作る。

https://qiita.com/rk05231977/items/9f4ce13d3cd285891fd7

# NFSサーバーを作る

環境がAWSなのでAmazon EFSで。Amazon EFSのコンソールから「ファイルシステムの作成」→「作成」とクリックすればNFSサーバーが出来る。
先の自作Kubernetesクラスターだと、kubeセキュリティグループにノードが配置されているので、EFSの「ネットワーク」>「管理」からセキュリティグループにkubeセキュリティグループを追加しておく。

|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/146381/35f35313-b789-a0e1-2fcb-29d8c99a8b78.png)|
|:|

|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/146381/d0780113-3271-a619-40a5-d9175e5e5d99.png)|
|:|

EFS画面右上の「アタッチ」から「IP経由でマウント」より、マウントオプション含めたnfsのマウントコマンドを取得する。
UbuntuだとEFSマウントヘルパーをコンパイルするのが難儀なので使わないが、AZ内通信でTLSが必要とか電気とCO2排出の無駄だろう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/146381/e76bbd0d-94d0-ddcf-0415-0924dc31fc1b.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/146381/c3b177f3-4344-91ad-5bd4-93f8e93135d9.png)

# NFSをマウントする

Kubernetesのワーカーノード（全台）で、nfsクライアントを入れてEFSをマウントする。
マウントは、先に入手したコマンドからマウントポイント（efs/の部分）だけ変えて実行。

```bash:
$ sudo apt install -y nfs-common
$ sudo mount -t nfs4 -o nfsvers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2,noresvport ＜EFSインスタンス＞:/ /mnt
```

# 動作確認

テスト用の、httpdのindex.htmlを作っておく。
以下を実行するのはワーカーノードの１台からだけでいい。
マスターノードにもnfsマウントしておいて、そこから操作を一元化するでもいい。

```bash:
$ sudo mkdir /mnt/httpd
$ sudo chmod 777 /mnt/httpd
$ echo nfs > /mnt/httpd/index.html
```

作ったNFS上のindex.htmlファイルを参照するようなhttpdのDeploymentリソースを作る。
クラスターにワーカーノードが２台あり、それらに分散配置するなら以下の通り。
ファイルはマスターノード上というか、kubectlを実行するマシン上に作る。

```yaml:deploy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: httpd
  labels:
    app: httpd
spec:
  replicas: 2
  selector:
    matchLabels:
      app: httpd
  template:
    metadata:
      labels:
        app: httpd
    spec:
      containers:
      - name: httpd
        image: httpd
        ports:
        - containerPort: 80
        volumeMounts:
        - mountPath: /usr/local/apache2/htdocs
          name: htdocs
      volumes:
      - name: htdocs
        hostPath:
          path: /mnt/httpd/
          type: Directory
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - httpd
            topologyKey: kubernetes.io/hostname
```

Deploymentリソースをデプロイする。
マスターノード上で以下を実行。

```bash:
$ kubectl create -f deploy.yaml
```

デプロイされたPodの一覧を内部IP込みで確認する。ついでに、各Podが異なるワーカーノードに配置されているのも確認。

```bash:
$ kubectl get pod -o wide
NAME                    READY   STATUS    RESTARTS   AGE    IP                NODE               NOMINATED NODE   READINESS GATES
httpd-b68dbf7b8-9cr2q   1/1     Running   0          7m7s   192.168.195.131   ip-172-31-19-124   <none>           <none>
httpd-b68dbf7b8-jh65k   1/1     Running   0          7m7s   192.168.67.66     ip-172-31-17-131   <none>           <none>
```

マスターノード上、あるいはワーカーノード等クラスターに参加しているOSからであれば、Podの内部IPに直接アクセス出来るので、curlでPodのIPアドレスを直接確認。
「nfs」と出力される。
つまりOSにマウントされたnfs上の領域にhostPathでマウント出来たのが確認できる。

```bash:
$ curl 192.168.195.131
nfs
$ curl 192.168.67.66
nfs
```

一方のPodから書き込んだファイルを他方のPodでも読み取れる事も確認できる。

```bash:
$ kubectl exec -it httpd-b68dbf7b8-9cr2q bash
# echo write > htdocs/write.html
# exit
$ curl 192.168.195.131/write.html
write
$ curl 192.168.67.66/write.html
write
```

# おわりに

KubernetesのPodがNFSに書き込みたいって、ユースケースとしては既存のアプリがstdoutじゃない場所に出力するログファイルをどうにか取っておきたいというのが多いと思うが、その場合はnfs領域上でPodレプリカ間のログファイル名が重複しないようにする工夫がひと手間必要になる。てか、NFSじゃなくてローカルディスク上のhostPath使ってローカルOS上のエージェントがCloudWatch Logsとかに転送して集約するのが多分ベスト。

データベースがNFS領域を共用するのは、まあ、クラスター外にクラウドが別途用意したAmazon RDSとかのDBインスタンス使うほうが良いんじゃないのと思う。

まあ、それでもファイルシステムにファイルが書き込めないと死んでしまうアプリというのは案外残っているもので、GitLabとかJenkinsもそうだ。個人的にはVMで動かせよと思うが。