📝

Elastic Beanstalk から EFS をマウントしてみた

に公開

Elastic Beanstalk 環境の EC2 インスタンスから EFS をマウントしてファイルを書き込んでみました。

EFS ファイルシステムの作成

以下以外はデフォルト設定で作成しました。

  • ファイルシステム名: 今回は test
  • VPC: デフォルト VPC

Elastic Beanstalk 環境用のアプリケーション

以下のファイル構成でアプリケーションを作成します

my-eb-efs-app/
├── .ebextensions/
│   ├── efs-mount.config
├── .platform/
│   └── hooks/
│       └── prebuild/
│           └── 01_mount_efs.sh
├── package.json
├── app.js
└── .npmrc

各ファイルの内容は以下の通りです。

efs-mount.config
# EFSの設定
option_settings:
  aws:elasticbeanstalk:application:environment:
    EFS_FILE_SYSTEM_ID: <作成済みの EFS ファイルシステム ID>
    EFS_MOUNT_DIRECTORY: /mnt/efs

packages:
  yum:
    amazon-efs-utils: []
01_mount_efs.sh
#!/bin/bash

# EFS設定を環境変数から取得
EFS_MOUNT_DIR=$(/opt/elasticbeanstalk/bin/get-config environment -k EFS_MOUNT_DIRECTORY)
EFS_FILE_SYSTEM_ID=$(/opt/elasticbeanstalk/bin/get-config environment -k EFS_FILE_SYSTEM_ID)

# EFSマウントディレクトリを作成
mkdir -p ${EFS_MOUNT_DIR}

# 既にマウントされているかチェック
if grep -q "${EFS_MOUNT_DIR}" /proc/mounts; then
    echo "EFS is already mounted."
else
    # EFSをマウント
    echo "Mounting EFS ${EFS_FILE_SYSTEM_ID} to directory ${EFS_MOUNT_DIR}"
    mount -t efs ${EFS_FILE_SYSTEM_ID}:/ ${EFS_MOUNT_DIR}
    
    # マウントが成功したか確認
    if [ $? -ne 0 ]; then
        echo "Failed to mount EFS."
        exit 1
    fi
fi

# アプリケーションユーザーに書き込み権限を付与
chmod 777 ${EFS_MOUNT_DIR}
package.json
{
  "name": "eb-efs-example",
  "version": "1.0.0",
  "description": "Simple app to write to EFS from Elastic Beanstalk",
  "main": "app.js",
  "scripts": {
    "start": "node app.js"
  },
  "dependencies": {
    "express": "^4.18.2"
  }
}
app.js
const express = require('express');
const fs = require('fs');
const path = require('path');
const app = express();
const port = process.env.PORT || 8080;

// EFSのマウントパス
const EFS_MOUNT_PATH = '/mnt/efs';

app.get('/', (req, res) => {
  res.send(`
    <h1>Elastic Beanstalk EFS Example</h1>
    <p>This app demonstrates writing to an EFS volume.</p>
    <form action="/write-file" method="get">
      <input type="text" name="content" placeholder="Enter content to write" required>
      <button type="submit">Write to EFS</button>
    </form>
    <p><a href="/list-files">List Files in EFS</a></p>
  `);
});

app.get('/write-file', (req, res) => {
  const content = req.query.content || 'Hello EFS from Elastic Beanstalk!';
  const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
  const filename = `file-${timestamp}.txt`;
  const filePath = path.join(EFS_MOUNT_PATH, filename);

  try {
    // EFSディレクトリが存在することを確認
    if (!fs.existsSync(EFS_MOUNT_PATH)) {
      fs.mkdirSync(EFS_MOUNT_PATH, { recursive: true });
    }

    // ファイルを書き込み
    fs.writeFileSync(filePath, content);
    
    res.send(`
      <h1>File Written Successfully</h1>
      <p>File: ${filename}</p>
      <p>Content: ${content}</p>
      <p><a href="/">Back to Home</a></p>
      <p><a href="/list-files">List Files in EFS</a></p>
    `);
  } catch (error) {
    res.status(500).send(`
      <h1>Error Writing File</h1>
      <p>Error: ${error.message}</p>
      <pre>${error.stack}</pre>
      <p><a href="/">Back to Home</a></p>
    `);
  }
});

app.get('/list-files', (req, res) => {
  try {
    // EFSディレクトリが存在することを確認
    if (!fs.existsSync(EFS_MOUNT_PATH)) {
      return res.send(`
        <h1>EFS Directory Not Found</h1>
        <p>The EFS mount point at ${EFS_MOUNT_PATH} does not exist.</p>
        <p><a href="/">Back to Home</a></p>
      `);
    }

    // ディレクトリ内のファイルを一覧表示
    const files = fs.readdirSync(EFS_MOUNT_PATH);
    
    let fileList = '';
    if (files.length === 0) {
      fileList = '<p>No files found in EFS.</p>';
    } else {
      fileList = '<ul>' + 
        files.map(file => {
          const filePath = path.join(EFS_MOUNT_PATH, file);
          const stats = fs.statSync(filePath);
          return `<li>${file} - ${stats.size} bytes (${new Date(stats.mtime).toLocaleString()})</li>`;
        }).join('') + 
        '</ul>';
    }
    
    res.send(`
      <h1>Files in EFS</h1>
      ${fileList}
      <p><a href="/">Back to Home</a></p>
      <p><a href="/write-file?content=Auto-generated content at ${new Date().toLocaleString()}">Write a Test File</a></p>
    `);
  } catch (error) {
    res.status(500).send(`
      <h1>Error Listing Files</h1>
      <p>Error: ${error.message}</p>
      <pre>${error.stack}</pre>
      <p><a href="/">Back to Home</a></p>
    `);
  }
});

app.listen(port, () => {
  console.log(`App listening at http://localhost:${port}`);
});
.npmrc
unsafe-perm=true

以下のコマンドで zip 化します。

# アプリケーションのルートディレクトリで実行
$ zip -r my-eb-efs-app.zip .

Elastic Beanstalk 環境の作成

以下の設定で作成します。

  • プラットフォーム: Node.js 22 running on 64bit Amazon Linux 2023/6.5.1
  • コードは上記手順で作成した zip ファイルをアップロード
  • プリセット: 単一インスタンス
  • VPC: デフォルト VPC
  • CloudWatch ログへのインスタンスログのストリーミング: オン

数分でデプロイが完了します。
デプロイ完了後、Elastic Beanstalk のデフォルトドメインにアクセスして、以下の画面が表示されれば成功です。

エラーが発生した場合は CloudWatch Logs のログを確認してください。
主にセキュリティグループでのネットワーク疎通の問題や EFS ファイルシステム ID 指定ミスが考えられます。

動作確認

テキストボックスに任意の文字列を入力し、以下の画面が表示されれば書き込み成功です。

List Files in EFS をクリックすると書き込み済みのファイル一覧を表示できます。

また、EC2 インスタンスにセッションマネージャーなどでログインすることで、マウントしていることやファイルが作成されていることを確認できます。

$ df -h | grep efs
127.0.0.1:/       8.0E     0  8.0E   0% /mnt/efs

$ ls -la /mnt/efs
total 12
drwxrwxrwx. 2 root   root   6144 May 17 06:58 .
drwxr-xr-x. 3 root   root     17 May 17 06:40 ..
-rw-r--r--. 1 webapp webapp    4 May 17 06:48 file-2025-05-17T06-48-46-755Z.txt
-rw-r--r--. 1 webapp webapp    6 May 17 06:58 file-2025-05-17T06-58-53-341Z.txt

まとめ

今回は Elastic Beanstalk から EFS をマウントしてみました。
どなたかの参考になれば幸いです。

Discussion