1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

コンテナのデータを低コストで永続化するために Mountpoint for Amazon S3 で EC2 に S3 バケットを自動マウントする

1
Last updated at Posted at 2025-11-01

概要

Amazon EC2 インスタンスの起動時に S3 バケットを自動的にマウントできるようになったので、もうコンテナ内のデータを永続化する構成で悩む必要がなくなりました!

しかし設定箇所や注意点が多いので、以下の環境で IaC 化の要点をまとめました:

  • Amazon EC2 + Amazon Linux 2023
  • Docker Compose V2
  • AWS CloudFormation
  • Ansible

1. コンテナのデータを永続化する方式

A. コンテナ内のディレクトリ

何もしないとデータはコンテナ内のディレクトリに出力されるので、コンテナが破棄されるとデータも消滅します。

B. ホストのディレクトリをバインドマウントする

ホストのディレクトリをコンテナにバインドマウントすれば、コンテナが破棄されてもデータはホストに残ります。

compose.yaml
services:
  App:
    volumes:
      - /var/app/data:/data

しかし、ホストマシンが Amazon EC2 の場合は以下の問題があります:

  • :x: ホストの EC2 をストレージと共に破棄するとデータも消滅する
  • :x: ホスト以外からデータにアクセスできない
  • :x: EC2 のストレージ (EBS) は容量単価が高い
  • :x: ストレージの残容量やバックアップなどの管理が必要

C. S3 のバケットをマウントする

そこで EC2 に更に Amazon S3 のバケットをマウントすると、上記の問題が全て解決します。

  • :thumbsup: データを永続化できる
  • :thumbsup: データを共有できる
  • :thumbsup: 容量単価が安価
  • :thumbsup: ストレージの管理が不要

ただし S3 の性能特性は普通のブロックデバイスと異なるので、ユースケースに適した方式を S3 のストレージクラスや EFS (Elastic File System) 等から選定する必要があります。

2. 設定箇所とポイント

S3 バケットへの書き込みを許可を EC2 インスタンスと S3 バケットの両方に設定する必要があります。またコンテナ内の実行ユーザーが root 以外の場合は、fstab マウント時に Linux のパーミッションも設定する必要があります。

2.1. EC2 インスタンス: S3 の操作を許可する

バケットをマウントする EC2 インスタンスの IAM インスタンスプロファイルに、S3 の操作を許可した IAM ロールをアタッチします。

ec2-policy.png

2.2. S3 バケット: EC2 インスタンスからのアクセスを許可する

この IAM ロールを、S3 バケットにアタッチするポリシーの Principal に設定することで、同じ IAM ロールをアタッチした EC2 インスタンスがバケットを操作できるようになります。

バケットポリシー
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::123456789012:role/xxxx-instance-iam-role"
            },
            "Action": "s3:*",
            "Resource": [
                "arn:aws:s3:::xxxx_data_bucket",
                "arn:aws:s3:::xxxx_data_bucket/*"
            ]
        }
    ]
}

2.3. fstab マウント: Linux のパーミッションを設定する

ホストの /etc/fstab に、以下のように S3 マウントのエントリを追加します。このエントリは UID = 999 の Linux ユーザーに読み書き・上書き・削除の操作を許可する例です。

s3://xxxx_data_bucket/ /var/app/data mount-s3 _netdev,nosuid,nodev,nofail,rw,allow-other,allow-delete,allow-overwrite,uid=999,gid=999,dir-mode=775,file-mode=664 0 0

この例では、エントリの各フィールドに以下の値を設定しました:

フィールド 説明
s3://xxxx_data_bucket/ マウントするデバイス
/var/app/data マウントポイントのパス
mount-s3 ファイルシステムの種類
_netdev,nosuid, ... ,dir-mode=775,file-mode=664 マウントオプション
0 dump 対象フラグ (0 で対象外)
0 起動時の fsck 優先度 (0 で対象外)

マウントオプションは以下のオプションを組み合わせました。必要に応じて変更してください。

マウントオプション 説明
_netdev マウントするためにネットワークが必要
nosuid 設定されたユーザー ID ファイルを含めることができないように指定する
nodev 特別なデバイスを含めることができないように指定する
nofail マウントに失敗してもシステムを起動する
rw 読み書き可能
allow-other root と他のユーザーのアクセスを許可
allow-delete 削除の操作を許可
allow-overwrite 上書き保存の操作を許可
uid=999 所有者の UID
gid=999 所有者の GID
dir-mode=775 ディレクトリのパーミッション
file-mode=664 ファイルのパーミッション

mount-s3 --help で表示される Mountpoint for Amazon S3 のマウントオプション

Mount options:
      --read-only
          Mount file system in read-only mode

      --allow-delete
          Allow delete operations on file system

      --allow-overwrite
          Allow overwrite operations on file system

      --incremental-upload
          Enable incremental uploads and support for appending to existing objects

      --auto-unmount
          Automatically unmount on exit

      --allow-root
          Allow root user to access file system

      --allow-other
          Allow other users, including root, to access file system

      --uid <UID>
          Owner UID [default: current user's UID]

      --gid <GID>
          Owner GID [default: current user's GID]

      --dir-mode <DIR_MODE>
          Directory permissions [default: 0755]

      --file-mode <FILE_MODE>
          File permissions [default: 0644]

2.4. Docker サービス: マウントが完了するまで起動させない

マウントポイント /var/app/data のマウントが完了するまで Docker サービスが起動しないように、docker.service のユニットファイルに以下を追加します。

[Unit]
RequiresMountsFor=/var/app/data

設定箇所とポイントは以上です

3. IaC コードの実装

前章の設定を自動構成するために、AWS 上の構成を CloudFormation で、Linux 上の構成を Ansible で実装します。

3.1. EC2 インスタンス、IAM インスタンスプロファイル、IAM ロール

S3 を操作する権限を持つ IAM ロールを作成し、その IAM ロールを IAM インスタンスプロファイルにアタッチして、更にそのプロファイルを EC2 インスタンスにアタッチします。

Resources:
  # バケットをマウントする EC2 インスタンス
  XxxxInstance:
    Type: AWS::EC2::Instance
    Properties:
      # 直下で定義している IAM インスタンスプロファイルをアタッチする
      IamInstanceProfile: !Ref XxxxIamInstanceProfile
      # ......
      # その他のプロパティは省略

  # IAM インスタンスプロファイル
  XxxxIamInstanceProfile:
    Type: AWS::IAM::InstanceProfile
    Properties:
      Path: /
      Roles:
        # 直下で定義している IAM ロールをアタッチする
        - !Ref XxxxInstanceIamRole

  # IAM ロール
  XxxxInstanceIamRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: xxxx-instance-iam-role
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - ec2.amazonaws.com
            Action:
              - sts:AssumeRole
      Path: /
      ManagedPolicyArns:
        # S3 を操作する権限を与える
        - !Sub arn:${AWS::Partition}:iam::aws:policy/AmazonS3FullAccess

3.2. S3 バケット、バケットポリシー

上で作成した IAM ロールを S3 バケットポリシーに設定することで、上の EC2 インスタンスからのアクセスを許可することができます。

  # EC2 インスタンスにマウントする S3 バケット
  XxxxS3Bucket:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: xxxx_data_bucket  # バケット名
      OwnershipControls:
        Rules:
          - ObjectOwnership: ObjectWriter
      PublicAccessBlockConfiguration:
        BlockPublicAcls: true
        BlockPublicPolicy: true
        IgnorePublicAcls: true
        RestrictPublicBuckets: true

  # その S3 バケットにアタッチするバケットポリシー
  XxxxS3BucketPolicy:
    Type: AWS::S3::BucketPolicy
    Properties:
      Bucket: !Ref XxxxS3Bucket  # バケット名 'xxxx_data_bucket'
      PolicyDocument:
        Version: 2012-10-17
        Statement:
            Action: s3:*
            Effect: Allow
            Principal:
              AWS:
                # EC2 インスタンスの IAM インスタンスプロファイルに
                # アタッチした IAM ロール
                - !GetAtt XxxxInstanceIamRole.Arn
            Resource:
              # ${XxxxS3Bucket} はバケット名 'xxxx_data_bucket'
              - !Sub arn:aws:s3:::${XxxxS3Bucket}
              - !Sub arn:aws:s3:::${XxxxS3Bucket}/*

3.3. Docker と Compose のインストール

以下、Amazon Linux 2023 に Ansible を使用してインストールします

Compose プラグインは Amazon Linux 2023 のリポジトリで提供されていなかったので、GitHub からダウンロードしてインストールします。

tasks/main.yml
- name: Docker を rpm パッケージでインストールする
  ansible.builtin.dnf:
    name:
      - docker
    state: present
    update_cache: true

- name: Docker のプラグイン用ディレクトリを作成する
  ansible.builtin.file:
    mode: "0755"
    path: /usr/local/libexec/docker/cli-plugins
    state: directory

- name: Docker Compose をダウンロードしてプラグイン用ディレクトリにインストールする
  ansible.builtin.uri:
    dest: /usr/local/libexec/docker/cli-plugins/docker-compose
    mode: "0755"
    status_code: [200, 304]
    url: https://github.com/docker/compose/releases/download/v2.39.4/docker-compose-linux-x86_64

3.4. Mountpoint for Amazon S3 のインストールと自動マウント

tasks/main.yml
- name: Mountpoint for Amazon S3 rpm package をインストールする
  ansible.builtin.dnf:
    name: "https://s3.amazonaws.com/mountpoint-s3-release/latest/x86_64/mount-s3.rpm"
    state: present

- name: マウントポイントを作成する
  ansible.builtin.file:
    path: "{{ s3_mountpoint }}"
    state: directory
    mode: "0777"

- name: S3 マウントのエントリを /etc/fstab に追加する
  ansible.posix.mount:
    boot: true
    fstype: mount-s3
    opts: "_netdev,nosuid,nodev,nofail,rw,allow-other,allow-delete,allow-overwrite,uid=999,gid=999,dir-mode=775,file-mode=664"
    path: "{{ s3_mountpoint }}"
    src: "s3://xxxx_data_bucket/"
    state: mounted

3.5. マウント完了まで Docker サービスを起動させない

Docker サービスのユニット設定を追加します。ユニットファイル自体を変更する代わりに、追加設定だけを定義したファイル requiremount.conf/etc/systemd/system/docker.service.d/ に配置します。

tasks/main.yml
- name: マウントが完了するまで Docker サービスを起動させない設定を追加する
  ansible.builtin.template:
    dest: /etc/systemd/system/docker.service.d/requiremount.conf
    mode: "0644"
    src: "{{ role_path }}/templates/requiremount.conf.j2"
  notify:
    - configuration updated

配置されるファイルの元になるテンプレートです

templates/requiremount.conf.j2
[Unit]
RequiresMountsFor={{ s3_mountpoint }}

S3 マウントポイントのパスを変数として定義します

group_vars/all.yml
# Ansible 用の変数
ansible_ssh_private_key_file: ~/.ssh/xxxx-ec2-ssh-key.pem
ansible_user: ec2-user
ansible_become: true

# S3 マウントポイントのパス
s3_mountpoint: /var/app/data

3.6. Compose ファイルのデプロイ

ホストのマウントポイントを compose.yaml でコンテナにバインドマウントします

tasks/main.yml
- name: Compose ファイルをデプロイする
  ansible.builtin.template:
    dest: /path/to/project/compose.yaml
    mode: "0644"
    src: "{{ role_path }}/templates/compose.yaml.j2"
  notify:
    - configuration updated

配置されるファイルの元になるテンプレートです

templates/compose.yaml.j2
---
services:
  App:
    volumes:
      - {{ s3_mountpoint }}:/data
  # ...
  # その他の項目は省略

S3 への書き込み遅延が許容できる場合は - {{ s3_mountpoint }}:/data の後に :delegated を追加することで、書き込み性能の向上が期待できます。

3.7. Docker サービスの自動起動と再起動

ユニットファイルをリロードして Docker サービスを再起動し、OS 起動時のサービス自動起動を設定します。

handlers/main.yml
- name: Reload unit files and restart docker service
  ansible.builtin.systemd_service:
    daemon_reload: true
    name: docker
    enabled: true
    state: restarted
  listen:
    - configuration updated

以上で S3 バケット s3://xxxx_data_bucket が EC2 インスタンスのマウントポイント /var/app/data に自動的にマウントされ、コンテナ内のユーザー UID=999 が読み書きできるようになります。

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?