EFSのマウントについて理解する。

# はじめに
先日、仕事で`EFS`を使うことがあったのですが、マウント周りの仕様を理解できておらずハマったので、しっかりと理解するために記事にまとめてみようと思います。

# Elastic File Systemについて
AWSが提供するファイルストレージサービスとなり、ざっくり言えば`NFS`サーバを提供するAWSサービスとなります。

**マウントターゲット**で各アベイラビリティゾーンから`EFS`へアクセスするためのエンドポイントを設定し、**アクセスポイント**でクライアントから`EFS`をマウントする際のディレクトリや権限を指定します。

**アクセスポイント**は`EFS`側の領域を分割してクライアントへ提供するための機能となるため、特に領域分割せず、`EFS`の領域をすべて使用する場合はアクセスポイントは使用しなくても良いです。

以下に`EFS`マウント設定概要図を示します。

![efs.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569912/150d3491-b85c-9ff1-8e99-bdc43274d7a7.png)

以下よりクライアント側、`EFS`側で設定する内容を記載します。

- [クライアント側の設定](#クライアント側の設定)
- [EFS側の設定](#efs側の設定)
    - [ファイルシステムの設定](#ファイルシステムの設定)
    - [アクセスポイントの設定](#アクセスポイントの設定)
        - [POSIXユーザ・グループ](#posixユーザグループ)
        - [ルートディレクトリ作成のアクセス許可](#ルートディレクトリ作成のアクセス許可)

## クライアント側の設定
クライアント側から`EFS`をマウントする場合、`Linux`等に標準でインストールされている`mount`コマンドに各種パラメータを指定するだけでも`EFS`をマウントできますが、**EFSマウントヘルパー**と呼ばれるパッケージをインストールすることで、マウント時のパラメータ指定等を簡略化することができます。

また、後述する**アクセスポイント**の設定を使用してマウントするためには**EFSマウントヘルパー**をインストールする必要があります。

- [アクセスポイントを使ったファイルシステムのマウント](https://docs.aws.amazon.com/ja_jp/efs/latest/ug/mounting-access-points.html)

**EFSマウントヘルパー**を入れないと、アクセスポイントで指定した領域・権限でのアクセスができないため、`EFS`を使用するのであれば、**EFSマウントヘルパー**はインストールしておいたほうが良いでしょう。

## EFS側の設定
`EFS`の設定ではファイルシステムとアクセスポイントの作成を行う必要がありますが、それぞれ何を設定するものなのかをまとめます。

### ファイルシステムの設定
以下のような`EFS`の基本設定を行います。

- EFSストレージクラス設定
- EFSバックアップ設定
- 暗号化有無設定
- マウントターゲット設定
- ファイルシステムポリシー設定

マウントターゲットはどのAZ（アベイラビリティーゾーン）にエンドポイントを作成するか、ファイルシステムポリシーは転送時暗号化強制や匿名アクセス禁止等のマウント時のポリシー設定が行なえます。

### アクセスポイントの設定
クライアントから`EFS`にアクセスする際の`UID`（ユーザID）・`GID`（グループID）やEFS側のマウントポイントの設定等を行います。

アクセスポイントの設定を行うことで、`EFS`の特定の領域のみクライアント側からアクセスさせることができます。

クライアント側から見た場合、例えば前述の図の`efs2`というアクセスポイントをマウントすると、`EFS`側のディレクトリは`/data/efs2`となりますが、クライアント側は`chroot`を使ったときのようにルートディレクトリを直接マウントしているように見えます。

#### POSIXユーザ・グループ
`EFS`の領域に書き込みを行う際、**クライアント側の`UID`、`GID`ではなく、POSIXユーザで指定した`UID`、`GID`でアクセス**を行います。

POSIXユーザを指定することで`EFS`の領域にあるファイルはすべてPOSIXユーザ・グループで指定した`UID`・`GID`に固定できますが、ファイルのオーナー・グループをPOSIXユーザ・グループで固定したくない場合は、設定しないことでクライアント側の`UID`、`GID`にすることもできます。

![posix.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569912/1449d7b8-4d04-93c1-7daa-a7d8b7da144f.png)

また、セカンダリグループIDを指定することで、セカンダリグループIDで指定したIDからのアクセスも許可することができるため、例えば以下のように`efs2`の親ディレクトリが`efs1`でそれぞれクライアントごとにアクセスポイントが異なる場合、`efs1`からは`/efs1/efs2`ディレクトリの配下にもファイル・ディレクトリ作成可能だが、`efs2`からは`efs1`で作成したファイル・ディレクトリにアクセスできないようなことが可能です。

![セカンダリGID.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569912/093711b2-48fc-ce72-fe2e-5ca5cdd257f2.png)

#### ルートディレクトリ作成のアクセス許可
`EFS`側にアクセスポイントの設定で指定したディレクトリパスが存在しない場合に、「**ルートディレクトリ作成のアクセス許可**」で指定した`UID`、`GID`、パーミッションでディレクトリを作成します。

存在しなかった場合にディレクトリを作ってくれるだけなので、あらかじめ手動でディレクトリを作成していたり、EFSマウント後に手動でディレクトリを作成する場合は指定しなくても問題ありません。

![自動作成.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569912/5a6a0ac2-df32-dc59-7994-6a6672be7e62.png)

## EFSのマウントについて
上記まででクライアント側、EFS側の設定についてまとめたため、以下ではEFSをマウントした際の動きについてまとめます。

- [EFSのルートディレクトリ権限](#efsのルートディレクトリ権限)
- [マウントした際の権限](#マウントした際の権限)
- [アクセスポイントを使わないマウント](#アクセスポイントを使わないマウント)
- [ルートディレクトリを直接マウントする場合の注意点](#ルートディレクトリを直接マウントする場合の注意点)

### EFSのルートディレクトリ権限
`EFS`側のルートディレクトリはユーザ＆グループが`root:root`、パーミッションは`0755`となります。

### マウントした際の権限
これは`EFS`というよりUNIX系OSでマウントした際の仕様となりますが、例えば`/mnt`に`NFS`領域をマウントした場合、`/mnt`がユーザ＆グループID`1001:1001`、パーミッション`0777`としていたとしても`EFS`をマウントした際には`EFS`側の権限となります。

アクセスポイントを使わない、もしくはアクセスポイント設定でディレクトリパスやPOSIX設定を行っていない場合、「**[EFSのルートディレクトリ権限](#efsのルートディレクトリ権限)**」で記載した権限となります。

```bash:EFSマウント前のユーザ＆グループID＆パーミッション
$ ls -ld /mnt
drwxrwxrwx. X 1001 1001 X XXX DD hh:mm /mnt
```

```bash:EFSマウント後のユーザ＆グループID＆パーミッション
$ ls -ld /mnt
drwxr-xr-x. X root root X XXX DD hh:mm /mnt
```

### アクセスポイントを使わないマウント
**EFSマウントヘルパー**をインストールしていなかったり、アクセスポイントの設定をしていない場合でも、`EFS`の領域をマウントすることができます。

その場合、`EFS`のルートディレクトリをマウントすることになり、またPOSIXユーザ設定も行えないため、`EFS`領域へアクセスする際のユーザ・グループでファイル・ディレクトリの読み書きすることになります。

### ルートディレクトリを直接マウントする場合の注意点
ルートディレクトリマウント時、上述の権限となるため、`root`以外では`EFS`領域に書き込みを行えません。

また、アクセスポイントの設定でPOSIXユーザの設定を行うと、POSIXユーザで指定した`UID`、`GID`でアクセスするため、書き込みに失敗します。

もしどうしてもルートディレクトリをマウントして、POSIXユーザ設定も使用したい場合は、`EFS`領域をマウント後、ルートユーザで`EFS`のルートディレクトリの権限をPOSIX設定で指定したユーザがアクセスできる権限に変更する必要があります。

```bash:ルートディレクトリマウント＆POSIXユーザ指定で使用したい場合の事前作業
$ sudo mount -t efs -o tls fs-xxxxxxxxxxxxxxxxx:/ /mnt
$ ls -ld /mnt
drwxr-xr-x. X root root X XXX DD hh:mm /mnt
$ chmod 777 /mnt
$ ls -ld /mnt
drwxrwxrwx. X root root X XXX DD hh:mm /mnt
```

## 動作確認
`EFS`の設定やマウント時の動作についてまとめてきましたが、実際にどのように見えるかをいくつかのシナリオを通して改めて確認してみようと思います。

今回はマウントテスト用としてEC2インスタンスを用意してそちらで以下のシナリオを確認していこうと思います。

- [事前準備](#事前準備)
- [シナリオ1：アクセスポイントの設定無しでマウントできるか？](#シナリオ1アクセスポイントの設定無しでマウントできるか)
- [シナリオ2：EFSのルートディレクトリをマウントしてec2-userで書き込みできるか？](#シナリオ2efsのルートディレクトリをマウントしてec2-userで書き込みできるか)
- [シナリオ3：EFSのルートディレクトリをマウントしてrootで書き込みできるか？](#シナリオ3efsのルートディレクトリをマウントしてrootで書き込みできるか)
- [シナリオ4：アクセスポイント設定でディレクトリ作成設定を行った際、ディレクトリ作成されるか？](#シナリオ4アクセスポイント設定でディレクトリ作成設定を行った際ディレクトリ作成されるか)
- [シナリオ5：POSIXユーザ設定を行った際、指定のユーザでファイル作成されるか？](#シナリオ5posixユーザ設定を行った際指定のユーザでファイル作成されるか)
- [シナリオ6：EFSルートディレクトリ側からアクセスポイントのディレクトリにファイル作成した場合はどうなるか？](#シナリオ6efsルートディレクトリ側からアクセスポイントのディレクトリにファイル作成した場合はどうなるか)

### 事前準備
`EFS`をマウントするためにEC2インスタンスにアタッチするセキュリティグループで`NFS`を許可しておきます。

また、アクセスポイントの機能を使用するため、EFSマウントヘルパーをインストールしておきます。

- [Amazon EFS クライアントの手動インストール](https://docs.aws.amazon.com/ja_jp/efs/latest/ug/installing-amazon-efs-utils.html)

### シナリオ1：アクセスポイントの設定無しでマウントできるか？
EC2インスタンス側にEFSをマウントするためのマウントポイントを作成しておきます。

```bash:マウントポイントの作成
$ sudo mkdir /efs1 && ls -ld /efs1
drwxr-xr-x. X root root X XXX  DD hh:mm /efs1
```

「**Elastic File System**」→「**ファイルシステム**」より作成済みのファイルシステムの選択し、右上の「**アタッチ**」を選択すると以下のようなマウントコマンドが表示されるため、作成したマウントポイントに合わせて修正しつつ実行します。

![Monosnap_20230505_104509.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569912/1faeeb62-d95b-5472-bbfa-dddd32676dc6.png)

```bash:EFSのマウント
$ sudo mount -t efs -o tls fs-xxxxxxxxxxxxxxxxx:/ /efs1
$ ls -ld /efs1
drwxr-xr-x. X root root X XXX DD hh:mm /efs1
```

元々のディレクトリ権限と同じなので分かりづらいですが、EFSをマウントしているため、上記は`EFS`側の権限となっています。

`df`コマンドを実行し、以下のように表示されていればマウント成功です。

```bash:マウント確認
$ df -h /efs1
Filesystem      Size  Used Avail Use% Mounted on
127.0.0.1:/     8.0E     0  8.0E   0% /efs1
```

### シナリオ2：EFSのルートディレクトリをマウントしてec2-userで書き込みできるか？
`EFS`のルートディレクトリは`root:root`、`0755`の権限となるため、結果としては、`Permission denied`となります。

```bash:ec2-userでEFS領域に書き込み
$ touch /efs1/testfile
touch: cannot touch '/efs1/testfile': Permission denied
```

### シナリオ3：EFSのルートディレクトリをマウントしてrootで書き込みできるか？
`root`であれば書き込み権限があるため、問題なく作成できます。

```bash:rootでEFS領域に書き込み
$ sudo touch /efs1/testfile
$ ls -la /efs1/testfile
-rw-r--r--. X root root X XXX DD hh:mm /efs1/testfile
```

### シナリオ4：アクセスポイント設定でディレクトリ作成設定を行った際、ディレクトリ作成されるか？
今回は以下のようにアクセスポイントを作成します。

|設定項目|設定内容|備考|
|:--|:--|:--|
|ファイルシステム|作成済みのEFSファイルシステムID||
|名前|efs_ap|任意の名前を指定|
|ルートディレクトリパス|/efsdata|クライアント側に見せるEFS側ディレクトリパス|
|POSIXユーザID|2000|テストのため今回は2000を指定|
|POSIXグループID|2000|テストのため今回は2000を指定|
|POSIXセカンダリグループID|(空欄)|今回は未指定|
|所有者ユーザID|2000|POSIX設定で指定したIDと同じものを指定|
|所有者グループID|2000|POSIX設定で指定したIDと同じものを指定|
|アクセスポイントのアクセス許可|0755||
|タグ|(空欄)|今回は未指定|

また、ルートディレクトリをマウントしている`/efs1`とアクセスポイントの設定でマウントした際の違いを比較するため、アクセスポイントマウント用のディレクトリも作成しておきます。

```bash:アクセスポイントマウント用マウントポイントの作成
$ sudo mkdir /efs2 && ls -ld /efs2
drwxr-xr-x. X root root X XXX  DD hh:mm /efs2
```

アクセスポイントで指定した領域をマウントする場合も、「**Elastic File System**」→「**アクセスポイント**」から作成したアクセスポイントを選択し、右上の「**アタッチ**」を選択することでマウントコマンドを確認できます。

なお、前述の通り、アクセスポイントのマウントはEFSマウントヘルパーをインストールしておかないとマウントできないので、コマンドはEFSマウントヘルパーでのマウントコマンド例しか表示されません。

![Monosnap_20230505_120019.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569912/ad0caa3c-558b-8509-c0d7-fb0aef8af897.png)

```bash:アクセスポイントでのEFSのマウント
$ sudo mount -t efs -o tls,accesspoint=fsap-xxxxxxxxxxxxxxxxx fs-xxxxxxxxxxxxxxxxx:/ /efs2
```

マウント後、`/efs1`にマウントしているルートディレクトリからの確認と、`/efs2`にマウントしているアクセスポイントで指定したディレクトリからの確認を行った結果が以下となります。

```bash:ルートディレクトリから確認した結果
$ ls -l /efs1
total x
drwxr-xr-x.  X 2000 2000 X XXX DD hh:mm efsdata
```

```bash:アクセスポイントから確認した結果
$ ls -l /efs2
total 0
```

EFSのルートディレクトリから確認した場合、「**所有者ユーザID**」、「**所有者グループID**」、「**アクセスポイントのアクセス許可**」で設定した権限でディレクトリが自動的に作られていることが確認できます。

また、アクセスポイントでのマウントとなる`/efs2`からの確認では、`EFS`側の`/efsdata`がマウントされているため、まだ何も存在しないことが確認できます。

### シナリオ5：POSIXユーザ設定を行った際、指定のユーザでファイル作成されるか？
POSIX設定を行っている場合、POSIXで指定しているユーザID、グループIDでEFS領域に書き込まれるはずなので、`ec2-user`で作成したファイルを`cp -p`でEFS領域にコピーして、POSIXで設定したユーザID、グループIDになるかを確認してみます。

`ec2-user`のホームディレクトリに`ec2userfile`というテストファイルを作成して、`/efs2`にコピーした結果が以下となります。

```bash:テスト用ファイルの作成
$ touch ec2userfile && ls -l ec2userfile
-rw-rw-r--. 1 ec2-user ec2-user X XXX DD hh:mm ec2userfile
```

```bash:テスト用ファイルのEFS領域へのコピー
$ cp -p ec2userfile /efs2/ec2userfile
$ ls -l /efs2/ec2userfile
-rw-rw-r--. 1 2000 2000 X XXX DD hh:mm /efs2/ec2userfile
```

`cp -p`で権限を保持したコピーを行っても、POSIXで指定したユーザID、グループIDに変更されていることが確認できます。

また、先程`/efsdata`のディレクトリを`2000:2000`、`0755`の権限で作成しているため、`Permission denied`にならずに正常に書き込めていることが確認できました。

### シナリオ6：EFSルートディレクトリ側からアクセスポイントのディレクトリにファイル作成した場合はどうなるか？
結論から言ってしまうと、ルートディレクトリをマウントしている`/efs1`はアクセスポイントで設定している権限等の設定は有効にならないので、クライアント側のユーザで作られます。

また、`ec2-user`の場合、`UID`・`GID`が`1000:1000`なので`/efsdata`配下には作成できずに失敗します。

```bash:ec2-userでEFSルートディレクトリからアクセスポイントディレクトリへのファイル作成
$ touch /efs1/efsdata/ec2testfile
touch: cannot touch '/efs1/efsdata/ec2testfile': Permission denied
```

`root`で作成した場合は問題なく作成でき、アクセスポイント経由ではないので、`UID`・`GID`が変換されることなく作成されます。

```bash:rootでEFSルートディレクトリからアクセスポイントディレクトリへのファイル作成
$ sudo touch /efs1/efsdata/roottestfile
$ ls -l /efs1/efsdata/roottestfile
-rw-r--r--. X root root X XXX DD hh:mm /efs1/efsdata/roottestfile
```

## アクセスポイントを使用したマウント構成の検討
`EFS`のマウントについてまとめ。

- EFSのルートディレクトリ権限は`root:root`、`0755`。
- ルートディレクトリマウントではルートユーザ以外書き込めない。
- POSIX設定を行うと、指定した`UID`、`GID`で強制される。
- 「**ルートディレクトリ作成のアクセス許可**」を指定するとマウント時に指定した権限でディレクトリ作成。

`EFS`のルートディレクトリを使用する場合、手動でルートディレクトリの権限を変更したりしなければならず、良い手段とは言えないため、「**ルートディレクトリ作成のアクセス許可**」でマウント時に指定した権限のディレクトリを作成するようにした上で、クライアント側からマウントして使うのが良いかと思います。

# おわりに
一度イメージできてしまえば簡単なことでも、アクセスポイントやEFSのマウント方法についてイメージできていないと、私のように想定通りにアクセスできずにハマってしまいます。

同じようにハマる人の助けになれば幸いです。