CFNでEFS入門

CloudFormationでEFS入門

AWS SAAの出題範囲の1つである、高性能なアーキテクチャの設計に関する内容です。

EFSはAWSが提供するストレージサービスの1種です。

Amazon Elastic File System (Amazon EFS) は、ストレージをプロビジョニングまたは管理することなくファイルデータを共有できる、シンプルでサーバーレスのセットアンドフォーゲットのエラスティックファイルシステムを提供します。 (中略) アプリケーションを中断することなくオンデマンドでペタバイトまで拡張できるように構築されています。

Amazon EFS の特徴

今回は入門編ということで、CloudFormationでEFSを構築し、2つのEC2インスタンスからアクセスすることを目標とします。

構築する環境

Diagram of introduction to EFS.

VPC内に2つのサブネットを作成します。
いずれもインターネットにアクセス不可なプライベートサブネットとします。

EFSを作成し、各サブネットに関連付けます。

また各サブネットにEC2インスタンスを1つずつ配置します。
EFSにアクセスするために使用します。
インスタンスは最新版のAmazon Linux 2とします。

SSMエンドポイントを作成します。
本エンドポイントは2つの用途で使用します。
1点目はSSM Session Managerを使用して、EC2インスタンスにリモートアクセスするためです。
2点目はSSMドキュメントを使用して、インスタンスの初期化処理を実行するためです。

S3エンドポイントも作成します。
S3バケット上に構築されるyumリポジトリにアクセスするために使用します。

CloudFormationテンプレートファイル

上記の構成をCloudFormationで構築します。
以下のURLにCloudFormationテンプレートを配置しています。

https://github.com/awstut-an-r/awstut-saa/tree/main/02/007

テンプレートファイルのポイント解説

EFS用セキュリティグループ

Resources:
  InstanceSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupName: !Sub "${Prefix}-InstanceSecurityGroup"
      GroupDescription: Deny All.
      VpcId: !Ref VPC

  EFSSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupName: !Sub "${Prefix}-EFSSecurityGroup"
      GroupDescription: Allow NFS from InstanceSecurityGroup.
      VpcId: !Ref VPC
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: !Ref NFSPort
          ToPort: !Ref NFSPort
          SourceSecurityGroupId: !Ref InstanceSecurityGroup
Code language: YAML (yaml)

インスタンスとEFS間の通信はNFSで行われます。

Amazon EFS ファイルシステムは、Network File System バージョン 4.0 および 4.1 (NFSv4) プロトコルを使用して、仮想プライベートクラウド (VPC) にマウントできます。

Amazon EFS: 仕組み

インスタンスがEFSとNFS通信するためには、後述のEFSマウントターゲットに、以下を満たしたセキュリティグループを関連づける必要があります。

マウントターゲットに関連付けるセキュリティグループは、ファイルシステムをマウントするすべての EC2 インスタンスから、NFS ポート上の TCP プロトコルへのインバウンドアクセスを許可する必要があります。

セキュリティグループの作成

上記に従い、インスタンスからのNFS通信(2049/tcp)を許可するようにセキュリティグループを作成します。

EFSの2つのモード

EFS本体の設定を確認します。

Resources:
  EFS:
    Type: AWS::EFS::FileSystem
    Properties:
      Encrypted: true
      PerformanceMode: generalPurpose
      ThroughputMode: bursting
Code language: YAML (yaml)

EFSでは2つのパラメータを設定する必要があります。

1つ目はパフォーマンスモードです。
汎用モードと最大 I/Oモードが選択可能ですが、前者がデフォルトのモードで、多くの用途に対応できます。

大部分の Amazon EFS ファイルシステムには、汎用パフォーマンスモードをお勧めします。汎用は、ウェブ配信環境、コンテンツ管理システム、ホームディレクトリ、一般的なファイルサービスなど、レイテンシーに敏感なユースケースに最適です。

Amazon EFS パフォーマンス

2つ目はスループットモードです。
バーストスループットとプロビジョニングされたスループットが選択可能です。
今回は前者を選択します。
バーストスループットはEBSの汎用SSDのように、クレジットに応じたバーストが可能なモードです。

ファイルベースのワークロードは通常高低差が激しく、短期間で高レベルのスループットが発生し、残りの時間は低レベルのスループットになります。これに対応するために、Amazon EFS は一定期間内のスループットレベルのバーストが許可されるように設計されています。

Amazon EFS パフォーマンス

上記に従い、PerformanceModeプロパティに「generalPurpose」を、ThroughputModeプロパティに「bursting」を指定します。

EFSはAZをまたがって配置できる

インスタンスがEFSにアクセスできるようにEFSマウントポイントを定義します。

Resources:
  MountTarget1:
    Type: AWS::EFS::MountTarget
    Properties:
      FileSystemId: !Ref EFS
      SubnetId: !Ref PrivateSubnet1
      SecurityGroups:
        - !Ref EFSSecurityGroup

  MountTarget2:
    Type: AWS::EFS::MountTarget
    Properties:
      FileSystemId: !Ref EFS
      SubnetId: !Ref PrivateSubnet2
      SecurityGroups:
        - !Ref EFSSecurityGroup
Code language: YAML (yaml)

マウントターゲットはサブネット単位で作成する必要があります。
今回は2つのサブネットにインスタンスを配置しますので、サブネットごとにマウントターゲットを作成します。

またマウントポイントにはセキュリティグループを関連づけます。
先述のマウントポイント用セキュリティグループを指定します。

SSM Documentを使ってインスタンスをセットアップする

インスタンスからEFSにアクセスするための初期化処理を定義します。

Resources:
  RunShellScriptAssociation:
    Type: AWS::SSM::Association
    Properties:
      AssociationName: !Sub ${Prefix}-shellscript-association
      Name: AWS-RunShellScript
      OutputLocation:
        S3Location:
          OutputS3BucketName: !Ref LogBucketName
          OutputS3KeyPrefix: !Sub "${Prefix}/shellscript-association-log"
      Parameters:
        commands:
          - "sudo yum update -y"
          - "sudo yum install -y amazon-efs-utils"
          - "sudo mkdir /mnt/efs"
          - "sleep 90"
          - !Sub "sudo mount -t efs ${FileSystemId}:/ /mnt/efs"
      Targets:
        - Key: InstanceIds
          Values:
            - !Ref Instance1
            - !Ref Instance2
      WaitForSuccessTimeoutSeconds: 300
Code language: YAML (yaml)

インスタンスがEFSにアクセスするためには、AWSが提供しているAmazon EFSクライアントを使用することがベストプラクティスです。

Amazon EFS クライアント (amazon-efs-utils) は、Amazon EFS ツールのオープンソースのコレクションです。 (中略) Amazon EFS クライアントには、マウントヘルパーおよび Amazon EFS ファイルシステムの転送時のデータ暗号化の実行を簡単にするツールが用意されています。

Overview

同クライアントをインスタンスの初期化処理の一環としてインストールします。
インスタンスを自動的にセットアップする方法はいくつかありますが、今回はSSMドキュメントAWS-RunShellScriptを使用して、シェルスクリプトを実行する形でセットアップを行います。

詳細は以下のページをご確認ください。

あわせて読みたい
Linuxインスタンスの初期化方法4選 【Linuxインスタンスを初期化する4つの方法】 EC2インスタンスの起動時に初期化処理を実行する方法を考えます。 EC2インスタンスを構築時に初期化する以下の4つの手法を...

セットアップ内容は以下の通りです。

  1. yumでインストール済みのパッケージをアップデートする
  2. Amazon EFS クライアントをインストールする
  3. EFS用のマウントポイントを作成する
  4. 90秒待機する
  5. EFSをマウントする

処理の中で待機を挟む理由ですが、マウントターゲット直後に発生する恐れがあるマウントエラーを回避するためです。

It can take up to 90 seconds after creating a mount target for the Domain Name Service (DNS) records to propagate fully in an AWS Region. If you’re programmatically creating and mounting file systems, for example with an AWS CloudFormation template, we recommend that you implement a wait condition.

File System Mount Fails Immediately After File System Creation

以上の内容を2つのインスタンスに対して実行するように設定します。

プライベートサブネット内でyumを実行する

先述のセットアップ処理の中で、yumによるアップデート・インストールに関する内容が含まれていました。
しかし2インスタンスはプライベートサブネット内に配置されているため、インターネットにアクセスできず、このままではyumに失敗します。
そこで今回は、S3用VPCエンドポイントを作成し、S3上に構築されたAmazon Linux用リポジトリにアクセスすることで、プライベートサブネット内からyumを実行します。

詳細につきましては以下のページをご確認ください。

あわせて読みたい
プライベートサブネットのインスタンスでyum/dnfを実行する 【プライベートサブネット内のインスタンスでyum/dnfを実行する構成】 プライベートサブネット内のインスタンスで、yum/dnfを実行する方法を確認します。 今回は以下の2...

環境構築

CloudFormationを使用して、本環境を構築し、実際の挙動を確認します。

CloudFormationスタックを作成し、スタック内のリソースを確認する

CloudFormationスタックを作成します。
スタックの作成および各スタックの確認方法については、以下のページをご確認ください。

あわせて読みたい
CloudFormationのネストされたスタックで環境を構築する 【CloudFormationのネストされたスタックで環境を構築する方法】 CloudFormationにおけるネストされたスタックを検証します。 CloudFormationでは、スタックをネストす...

各スタックのリソースを確認した結果、今回作成された主要リソースの情報は以下の通りです。

  • Instance1:i-091b12e8726df75c1
  • Instance2:i-0664bcef7e9712b79
  • EFS:fs-07e706669a91cd761

AWS Management Consoleからもリソースの作成状況を確認します。
まずEFSを確認します。

Detail of EFS 1.
Detail of EFS 2.

正常にEFSが作成されていることがわかります。

EFSが2つAZ上に作成されたサブネットと関連づいていることもわかります。
両サブネットにマウントターゲットが作成されたということを示しています。

次にSSMドキュメントの実行結果を確認します。

Result of SSM Document.

2台のインスタンスに対して正常に実行されたことがわかります。

実行時のログをS3バケットに保存するように設定しました。
保存されたログは以下の通りです。

Loaded plugins: extras_suggestions, langpacks, priorities, update-motd
No packages marked for update
Loaded plugins: extras_suggestions, langpacks, priorities, update-motd
Resolving Dependencies
--> Running transaction check
---> Package amazon-efs-utils.noarch 0:1.33.1-1.amzn2 will be installed
--> Processing Dependency: stunnel >= 4.56 for package: amazon-efs-utils-1.33.1-1.amzn2.noarch
--> Running transaction check
---> Package stunnel.aarch64 0:4.56-6.amzn2.0.3 will be installed
--> Finished Dependency Resolution

Dependencies Resolved

================================================================================
 Package               Arch         Version               Repository       Size
================================================================================
Installing:
 amazon-efs-utils      noarch       1.33.1-1.amzn2        amzn2-core       51 k
Installing for dependencies:
 stunnel               aarch64      4.56-6.amzn2.0.3      amzn2-core      148 k

Transaction Summary
================================================================================
Install  1 Package (+1 Dependent package)

Total download size: 199 k
Installed size: 626 k
Downloading packages:
--------------------------------------------------------------------------------
Total                                              1.6 MB/s | 199 kB  00:00
Running transaction check
Running transaction test
Transaction test succeeded
Running transaction
  Installing : stunnel-4.56-6.amzn2.0.3.aarch64                             1/2
  Installing : amazon-efs-utils-1.33.1-1.amzn2.noarch                       2/2
  Verifying  : amazon-efs-utils-1.33.1-1.amzn2.noarch                       1/2
  Verifying  : stunnel-4.56-6.amzn2.0.3.aarch64                             2/2

Installed:
  amazon-efs-utils.noarch 0:1.33.1-1.amzn2

Dependency Installed:
  stunnel.aarch64 0:4.56-6.amzn2.0.3

Complete!
Code language: plaintext (plaintext)

amazon-efs-utilsおよびこれに依存関係のあるパッケージがインストールされたことがわかります。

動作確認

準備が整いましたので、EC2インスタンスにアクセスします。

Instance1

まずInstance1にSSM Session Manager経由でアクセスします。

% aws ssm start-session --target i-091b12e8726df75c1

Starting session with SessionId: root-0a204dce1675cd00c

sh-4.2$
Code language: Bash (bash)

SSM Session Managerの詳細につきましては、以下のページをご確認ください。

あわせて読みたい
LinuxインスタンスにSSM Session Manager経由でアクセスする 【LinuxインスタンスにSSM Session Manager経由でアクセスする】 EC2インスタンスにSSM Session Manager経由でアクセスする構成を確認します。 Session Manager は完全...

次にAWS EFS クライアントがインストールされていることを確認します。

sh-4.2$ yum list installed | grep efs
amazon-efs-utils.noarch               1.33.1-1.amzn2                   @amzn2-core
Code language: Bash (bash)

正常にインストールされたことがわかります。

ではdfコマンドでディスク状況を確認します。

sh-4.2$ df -hT
Filesystem                                              Type      Size  Used Avail Use% Mounted on
devtmpfs                                                devtmpfs  178M     0  178M   0% /dev
tmpfs                                                   tmpfs     215M     0  215M   0% /dev/shm
tmpfs                                                   tmpfs     215M  412K  214M   1% /run
tmpfs                                                   tmpfs     215M     0  215M   0% /sys/fs/cgroup
/dev/nvme0n1p1                                          xfs       8.0G  1.5G  6.5G  19% /
/dev/nvme0n1p128                                        vfat       10M  3.8M  6.3M  38% /boot/efi
tmpfs                                                   tmpfs      43M     0   43M   0% /run/user/0
fs-07e706669a91cd761.efs.ap-northeast-1.amazonaws.com:/ nfs4      8.0E     0  8.0E   0% /mnt/efs
Code language: Bash (bash)

一番下の行がEFSです。
セットアップ処理で指定した通り、/mnt/efsにマウントされていることがわかります。
Sizeの項目が「8.0E」ですので、スケール前ですが、既に8エクサバイト確保されていることがわかります。

次にEFSに書き込みを実行し、ファイル共有について確認します。

sh-4.2$ cd /mnt/efs

sh-4.2$ sudo touch test.txt

sh-4.2$ ls
test.txt
Code language: Bash (bash)

EFSに対して正常に書き込むことができました。

Instance2

続いてInstance2にアクセスします。

% aws ssm start-session --target i-0664bcef7e9712b79

Starting session with SessionId: root-0ede646d6dca48f56

sh-4.2$ 
Code language: Bash (bash)

同様にディスク状況を確認します。

sh-4.2$ df -hT
Filesystem                                              Type      Size  Used Avail Use% Mounted on
devtmpfs                                                devtmpfs  178M     0  178M   0% /dev
tmpfs                                                   tmpfs     215M     0  215M   0% /dev/shm
tmpfs                                                   tmpfs     215M  412K  214M   1% /run
tmpfs                                                   tmpfs     215M     0  215M   0% /sys/fs/cgroup
/dev/nvme0n1p1                                          xfs       8.0G  1.5G  6.5G  19% /
/dev/nvme0n1p128                                        vfat       10M  3.8M  6.3M  38% /boot/efi
tmpfs                                                   tmpfs      43M     0   43M   0% /run/user/0
fs-07e706669a91cd761.efs.ap-northeast-1.amazonaws.com:/ nfs4      8.0E     0  8.0E   0% /mnt/efs
Code language: Bash (bash)

こちらでもEFSが/mnt/efsをマウントポイントしてマウントされていることがわかります。

最後に同ディレクトリを確認します。

sh-4.2$ ls /mnt/efs
test.txt
Code language: Bash (bash)

Instance1で設置したファイルを、Instance2から確認することができました。

まとめ

EFS入門ということで、CloudFormationでEFSを作成しました。
EFSを使用することで、EC2インスタンス間でデータ共有できることを確認しました。