AWS

LocalStackのS3バケットにDockerコンテナからMountpoint for Amazon S3で接続する

mackey

こんにちは。エンジニアのmackeyです。今回は、Mountpoint for Amazon S3を使用するアプリケーションの開発時に役立つ知識を紹介したいと思います。

背景

AWSのS3を用いるアプリケーションを開発していた際に、Mountpoint for Amazon S3(以下Mountpointと省略する場合あり)が一般公開され、アプリケーションの要件的にマッチしていたため採用することにしました。ただ、ローカル環境のテスト用にLocalStackを用いていたため、MountpointでLocalStackのS3バケットをマウントできないか試してみることにしました。その結果、LocalStackに対してもうまく動作することがわかったので、この記事では最小構成のサンプルでやり方を紹介したいと思います。

LocalStackについて

LocalStack は、AWSの各種サービスをローカルでシミュレートできるツールです。例えば、S3を使用するアプリケーションを開発している場合、LocalStackで作成したS3バケットを用いることで、ローカル環境ではAWS上のS3バケットに接続することなくS3のAPIの実行をテストすることができます。

Mountpointについて

Mountpoint for Amazon S3は2023年8月に一般公開となったAWS公式のツールです。公式ドキュメントの説明を以下にに引用します。

Mountpoint for Amazon S3 は、Amazon S3 バケットをローカルファイルシステムとしてマウントするための、高スループットのオープンソースファイルクライアントです。Mountpoint を使用すると、アプリケーションは、開く、読み取るなどのファイルシステム操作を通じて Amazon S3 に保存されているオブジェクトにアクセスできます。

https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/userguide/mountpoint.html

Mountpointを使用してS3バケットをマウントすることで、S3バケット上のファイルを、あたかもローカルのディレクトリ上に存在するかのように、ファイルシステム操作で取り扱えるようにできるようになります。

当ブログにも一般公開当時に書かれた紹介記事があるので、基本的な情報や使い方を知りたい方は、そちらも見ていただければと思います。

Docker環境の準備

今回使用するコードはこちらのGitHubリポジトリにアップロードしています。
実行環境はMacOSを想定しており、アプリケーション側のコードにはPythonを用いることにします。

全体構成

.
├── cmd
│   ├── read_file.py
│   └── write_file.py
├── compose.yml
├── Dockerfile
├── init_localstack.sh
└── mount_localstack_s3.sh

各設定ファイルの説明

version: "3.8"

services:
  app:
    build: .
    privileged: true
    volumes:
      - .:/app
  localstack:
    image: localstack/localstack:3.2.0
    ports:
      - "127.0.0.1:5566:4566"            # LocalStack Gateway
      - "127.0.0.1:5510-5559:4510-4559"  # external services port range
    environment:
      # LocalStack configuration: https://docs.localstack.cloud/references/configuration/
      - DEBUG=${DEBUG:-0}
    volumes:
      - "./init_localstack.sh:/etc/localstack/init/ready.d/init_localstack.sh"

Dockerコンテナとしては、LocalStack用のコンテナ(localstack)と、それに接続するためのアプリケーション用のコンテナ(app)の二つを用意します。

注目すべき点は、6行目の privileged: true です。

Mountpoint uses the Linux FUSE subsystem to mount its file system. Running a FUSE file system inside a Docker container requires giving the container elevated root-level privileges to the host instance.

https://github.com/awslabs/mountpoint-s3/tree/main/docker#docker-permissions-and-aws-container-services

とある通り、Dockerコンテナ上でMountpointを使用するには、特権モードで実行する必要があります。ホストコンピュータへのアクセス権限が付与されることになるので、コンテナ上ではありますが信頼できない外部リソースにはアクセスしないなど、セキュリティ面には十分注意を払ってください。

#!/bin/bash

echo "Start LocalStack init..."

awslocal s3 mb s3://bucket-test

LocalStackにS3バケットを作成するためにのスクリプトです。compose.ymlの18行目でこのファイルをマウントしており、LocalStackの起動時に実行されます。
(起動時スクリプトの詳細は https://docs.localstack.cloud/references/init-hooks/ を参照)

FROM --platform=linux/arm64 python:3.12-slim

RUN apt update && apt upgrade -y

# Install Mountpoint for Amazon S3
RUN apt-get install -y wget fuse libfuse2 sudo
RUN wget https://s3.amazonaws.com/mountpoint-s3-release/latest/arm64/mount-s3.deb
RUN sudo apt-get install ./mount-s3.deb

WORKDIR /app

CMD bash mount_localstack_s3.sh && bash

アプリケーションコンテナのDockerfileです。pythonのDockerイメージにMountpointをインストールしています。
MacOSで実行しているため、arm64向けの設定になっているのでご注意ください。(環境ごとのインストール方法の詳細はhttps://github.com/awslabs/mountpoint-s3/blob/main/doc/INSTALL.md を参照)

#!/bin/bash

MOUNT_DIRECTORY="/mnt/s3"
BUCKET_NAME="bucket-test"

echo "Start mount s3 bucket..."
mkdir -p $MOUNT_DIRECTORY
mount-s3 \
--allow-delete --allow-overwrite \
--force-path-style --endpoint-url http://localstack:4566 --no-sign-request \
$BUCKET_NAME $MOUNT_DIRECTORY

コンテナ起動時にMountpointを実行してS3バケットをマウントするためのスクリプトです。mount-s3を実行する際のオプションが重要なので、一つずつ解説します。(オプションの詳細は https://github.com/awslabs/mountpoint-s3/blob/main/doc/CONFIGURATION.md を参照)

まず、以下はファイル操作に関するオプションです。

  • --allow-delete : ファイルの削除を可能にする
  • --allow-overwrite : 既存のファイルを上書き可能にする(アップデートで追加されたオプションです)

そして、以下はLocalStackのバケットをマウントする際に必要なオプションです。

  • --force-path-style : Path-Styleでリクエストを送る
  • --endpoint-url : S3のエンドポイントを指定する
  • --no-sign-request : AWS認証を無効にする

LocalStackにおけるPath-StyleとVirtual Hosted-Styleに関しては、https://docs.localstack.cloud/user-guide/aws/s3/#path-style-and-virtual-hosted-style-requests を参照してください。今回の場合、Virtual Hosted-Styleだとうまくいかなかったので、force-path-styleを指定して、endpoint-urlには http://localstack:4566 を指定しています。

動作検証

appコンテナで実行するPythonスクリプトの準備

openを使用したファイル操作を行うスクリプトを準備しておきます。

import sys

S3_MOUNT_PATH = "/mnt/s3"

if len(sys.argv) < 2:
    print("Usage: python write_file.py <text>")
    sys.exit(1)

with open(S3_MOUNT_PATH + "/test.txt", "w") as f:
    f.write(" ".join(sys.argv[1:]) + "\n")

ファイルを書き込むためのスクリプトです。

S3_MOUNT_PATH = "/mnt/s3"

with open(S3_MOUNT_PATH + "/test.txt", "r") as f:
    print(f.read())

ファイルを読み込むためのスクリプトです。

実行してみる

$ docker compose build
(省略)

$ docker compose up -d localstack

[+] Running 1/2
 ⠧ Network mountpoint-s3-test_default         Created     1.7s
 ✔ Container mountpoint-s3-test-localstack-1  Started     1.6s

まずはLocalStackコンテナの起動です。コンテナのログを見ると、この時点でS3バケットが作成されていることを確認できます。

$ docker compose run --rm app
Start mount s3 bucket...
bucket bucket-test is mounted at /mnt/s3
root@5218519f757d:/app# python cmd/write_file.py Hello, MountPoint!
root@5218519f757d:/app# python cmd/read_file.py
Hello, MountPoint!

root@5218519f757d:/app# python cmd/write_file.py Overwrite file!
root@5218519f757d:/app# python cmd/read_file.py
Overwrite file!

root@5218519f757d:/app# ls /mnt/s3
test.txt
root@5218519f757d:/app# cat /mnt/s3/test.txt
Overwrite file!

appコンテナを起動すると、まずDockerfileのCMDで指定した mount_localstack_s3.sh が実行され、バケットがマウントされます。

その後、Pythonのスクリプトを実行し、S3バケットへの書き込みと読み込みと上書きが成功していることが確認できます。

また、lsやcatが使えることも確認できます。

おわりに

今回は、LocalStackのS3バケットにDockerコンテナからMountpoint for Amazon S3で接続する方法を紹介しました。
記事投稿時点ではWeb上ではこの方法を紹介する記事はなさそうだったので、お役に立てば幸いです。

AUTHOR
mackey
mackey
記事URLをコピーしました