LoginSignup
9
6

More than 1 year has passed since last update.

AWS Fargateでストレージを共有するバインドマウントとデータボリュームの違いを理解する

Posted at

はじめに

アプリケーションをコンテナ化してAWSのFargateで運用する場合、Django+Nginxの場合だとスタティックファイルの共有が必要となるが、仕組みをよく理解していなくて混乱していた。
後から考えればそういうことかと思えたけどもそのあたりを整理してみた。

やりたいこと

ざっくり構成図を描くとこんな感じ。本当はRDSとかECRとか使っているが省略。

test.png

2つのアベイラビリティゾーンにパブリックサブネットを配置してFargateをロードバランシングして運用。
1つのタスク内にDjangoとNginxの2コンテナを置く。
DjangoとNginx間でファイル(実際はディレクトリ)を共有する必要があり、左右のFargate間はロードバランシングしているため共有が必要となる。

結論からいうと

  • タスク内のコンテナ間での共有 → バインドマウント
  • サービス内のロードバランシングしているタスク間での共有 → データボリューム

となる。そのためタスク内に1コンテナしかない場合、バインドマウントは要らないし、ロードバランシングしないのであればデータボリュームは不要だ。

タスク内のコンテナ間での共有(バインドマウント)

理解を深めるためにもう少しシンプルにしてみるとこんな感じ。
そしてDjangoとNginxでディレクトリ(/var/local/django)を共有したい。

test1.png

詳しくは以下の説明、特に「Dockerfile 内のパスとその内容をコンテナに公開する」の箇所にある。

まずDockerfileだがそれぞれにVOLUME指定がいる。

django/Dockerfile
# pull official base image
FROM python:3.10-slim-bullseye

# get arg
ARG APP_DIR=/usr/src/app
ARG SHARED_DIR=/var/local/django
# UIDとGIDはnginxコンテナのnginxユーザに合わせる
ARG UID=101
ARG GID=101

# set environment variables
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1
ENV DEBIAN_FRONTEND noninteractive
ENV LANG=ja_JP.UTF-8

# set work directory
WORKDIR ${APP_DIR}

# copy
COPY ./app .
COPY ./requirements.txt .

# install dependencies
RUN apt-get update && apt-get install -y locales && \
    sed -i -E 's/# (ja_JP.UTF-8)/\1/' /etc/locale.gen && \
    locale-gen && \
    pip install --upgrade pip && \
    pip install -r requirements.txt && \
    rm requirements.txt && \
    groupadd nginx -g ${GID} && \
    useradd nginx -u ${UID} -g ${GID} --home=${APP_DIR} && \
    mkdir -p ${SHARED_DIR} && \
    chown -R nginx.nginx ${SHARED_DIR}

USER nginx

VOLUME ["${SHARED_DIR}"]
nginx/Dockerfile
FROM nginx:1.23-alpine

# get arg
ARG SHARED_DIR=/var/local/django

# ひょっとしたらmkdir不要かもしれない。
RUN rm /etc/nginx/conf.d/default.conf && \
    mkdir -p ${SHARED_DIR} && chown nginx:nginx ${SHARED_DIR}

COPY nginx.conf /etc/nginx/conf.d

VOLUME ["${SHARED_DIR}"]

次AWSのタスク定義のvolumesセクションで、名前を efs としてバインドマウントを定義。

  "volumes": [
    {
      "name": "efs",
    }
  ]

名前はなんでもいいが後でefsの設定をするのでこうしておいた。

そしてコンテナ定義

container_definition.json(必要部分だけ抜粋)
[
    {
      "name" : "nginx",
      },
      "mountPoints" : [
        {
          "containerPath" : "/var/local/django",
          "sourceVolume" : "efs"
        }
      ],
      "portMappings" : [
        {
          "protocol" : "tcp",
          "hostPort" : 80,
          "containerPort" : 80
        }
      ]
    },
    {
      "name" : "django",
      },
      "command" : ["gunicorn", "-w", "3", "-b", ":8000", "--access-logfile", "-", "--capture-output", "--log-level", "debug", "mysite.wsgi:application"],
      "mountPoints" : [
        {
          "containerPath" : "/var/local/django",
          "sourceVolume" : "efs"
        }
      ]
    }

余談だが、よく両方のportMappingsが記載されている例を見るがNginx側だけでよい。
またDjango、Nginx側の設定も忘れずに

Django側

mysite/settings.py(必要部分だけ抜粋)
SHARED_DIR = '/var/local/django'
STATIC_ROOT = os.path.join(SHARED_DIR, 'static')

Nginx側

nginx.conf(必要部分だけ抜粋)
  location /static/ {
    alias /var/local/django/static/;
  }

サービス内のタスク間での共有(データボリューム)

上記でコンテナ同士の共有ができたならば次はタスク間での共有を行う。
いくつか手法はあるようだがterraform使いの私は以下を参考にしてEFSを使った。もちろんAWSコンソールからやってもいい。

上記ではaws_efs_file_systemとアベイラビリティゾーンごとにaws_efs_mount_targetを2つ設定しているが、このままだとEFSのアクセスポイントが無かったので私は以下を追加した。

resource "aws_efs_access_point" "access" {
  file_system_id = aws_efs_file_system.efs.id
}

最後に

完成品を公開するわけにも行かないため断片的な情報なのでどこまで伝わるかわからないが、少しでも手助けになれば幸いである。
またコンテナ間の共有はバインドマウントは使わずにS3を使うように設計するほうがAWSらしいかもしれない。

9
6
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
9
6