14
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

FastAPI + nginx on ECS Fargate

Last updated at Posted at 2022-06-18

0. はじめに

以前投稿した本番稼働を意識したECS Fargateを用いたFastAPIの環境構築(ALB、SSM、BlueGreenデプロイ)という記事では、FastAPIをECS Fargateで動かすための流れについて説明しました。しかしnginxを前段に噛ませる構成ではありませんでした。今回、仕事にてFastAPIを用いかつnginxをかませる形でFargateに構成しましたので、備忘録も兼ねて設定内容を記述したいと思います。

1. 簡単な構成説明

簡単な構成説明ですが、
ALB - nginxコンテナ - FastAPIコンテナ
という形でALBがnginxにリクエストを分散し、nginxとFastAPIはFargate内でそれぞれコンテナとして稼働するという構成になります。

FastAPIをFargateで動かすにあたり、必ずしもnginxはかませる必要はないのですが、uvicornの公式によると、nginxとの連携については、以下の様な記述があります。1

Using Nginx as a proxy in front of your Uvicorn processes may not be neccessary, but is recommended for additional resiliance. Nginx can deal with serving your static media and buffering slow requests, leaving your application servers free from load as much as possible.

お作法的にもALBから直接アプリケーションサーバーにアクセスしに行くよりも、nginxを噛ませた方が丁寧で、アプリケーションサーバーの負荷も下げられるかと思います。2

2. ファイル構成

ファイル構成は以下になります。DockerfileはFargate用にDockerfile-ecs-fargateを作っています。また、今の構成では不要なのですが、後々の拡張性のためにdocker-composeを用いており、そのためのファイルとしてdocker-compose.ecs_fargate.ymlを用意しています。
また、ecs_fargate/nginxディレクトリ内にはECS Fargate向けのnginxのDockerファイルやFastAPI向けのnginxのconfを置いています。

.
├── Dockerfile-ecs-fargate
├── app
│   └── main.py
├── docker-compose.ecs_fargate.yml
├── ecs_fargate
│   └── nginx
│       ├── Dockerfile
│       └── fastapi.conf
├── poetry.lock
└── pyproject.toml

それではそれぞれのファイルの中身について見ていきましょう。

Dockerfile-ecs-fargate

FROM python:3.9-buster
ENV PYTHONUNBUFFERED=1

WORKDIR /src

RUN mkdir -p tmp

RUN pip install poetry

COPY pyproject.toml* poetry.lock* ./

RUN poetry config virtualenvs.in-project true
RUN poetry install

COPY . .

VOLUME /src
VOLUME /src/tmp

CMD ["poetry", "run", "uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000", "--uds", "tmp/uvicorn.sock"]
  • COPY pyproject.toml* poetry.lock* ./をしたあとにCOPY . .しているのは、poetry installでキャッシュを効かせるためになります。
  • ポイントとしては、最後の行のCMDで、"--uds", "tmp/uvicorn.sock"としているところになります。nginxとの連携をsocketで行うためです。

ecs_fargate/nginx/Dockerfile

FROM nginx:1.21.5
RUN apt-get update && \
    apt-get install -y apt-utils curl \
    locales && \
    echo "ja_JP.UTF-8 UTF-8" > /etc/locale.gen && \
    locale-gen ja_JP.UTF-8
ENV LC_ALL ja_JP.UTF-8
ADD ./ecs_fargate/nginx/fastapi.conf /etc/nginx/conf.d
EXPOSE 80
  • ポイントはADD ./ecs_fargate/nginx/fastapi.conf /etc/nginx/conf.dでFastAPI用のconfを読み込ませているところです。

ecs_fargate/nginx/fastapi.conf

server {
    listen 80 default_server;

    location / {
      proxy_set_header Host $http_host;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header X-Forwarded-Proto $scheme;
      proxy_set_header Upgrade $http_upgrade;
      proxy_set_header Connection $connection_upgrade;
      proxy_redirect off;
      proxy_buffering off;
      proxy_pass http://uvicorn;
    }
}

map $http_upgrade $connection_upgrade {
  default upgrade;
  '' close;
}

upstream uvicorn {
  server unix:///src/tmp/uvicorn.sock;
}
  • upstreamuvicornを定義しuvicorn.sockの場所を指定しています。

app/main.py

from fastapi import FastAPI

app = FastAPI()

@app.get("/")
def read_root():
    return {"Hello": "World"}

@app.get("/healthcheck")
async def healthcheck():
    return {"message": "healthcheck"}
  • 基本的なHello Worldおよび、ALBからのヘルスチェック用の/healthcheckのみエンドポイントを用意しています。

pyproject.toml

[tool.poetry]
name = "fastapi-sample"
version = "0.1.0"
description = ""
authors = ["someone <someone@example.com>"]

[tool.poetry.dependencies]
python = "^3.9"
uvicorn = "^0.17.6"
pydantic = "^1.9.1"
fastapi = "^0.78.0"

[tool.poetry.dev-dependencies]

[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"

2. ビルドおよびECSのタスク定義について

以上の様なファイル構成およびファイル内容であとはお好きな形でイメージをビルドしていけば良いのですが、私はdocker-composeを利用しました。3

docker-compose -f docker-compose.ecs_fargate.yml build

このコマンドを打つとディレクトリ名_appディレクトリ名_nginxといったイメージが作成されます。docker imagesコマンドで確認してみてください。
その後、イメージのタグづけ、ECRへのpushなどを行っていきます。ここでは割愛します。詳細は、ECRへの登録の「docker imageのタグ付け」以降をご覧ください。

タスク定義のコンテナの定義のところですが、今回はappnginxというコンテナ名で登録しました。
image.png
タスク定義の設定に関しては、nginxコンテナのポートマッピングのコンテナポートに80を、ボリュームソースのソースコンテナにappを設定します。この辺りは例えばdocker-compose.ymlでの設定内容をECSではここで設定してあげるということになるかと思います。
image.png
image.png

3. 最後に

最終的にはまっていたのはuvicorn.sockのところでした。今思えばRailsで環境構築した際にはpuma.socknginxと連携していたので、当然のことでした。また公式1にもserver unix:/tmp/uvicorn.sock;の記載があり、きちんと公式読めば解決した話でした(アクセス権限の問題でuvicorn.sockの置き場所は公式とは変更しています)。普段はRailsで開発しているのですが、この度Pythonで開発する必要があり、Railsでの知識が役立った良い例でした。引き続き精進していきます。

  1. https://www.uvicorn.org/deployment/#running-behind-nginx 2

  2. 私が社会人1年目にSIerの研修では「Web3層構造」について学びました。今もこれが一般的なのかなと思っていますが、最近は違ってきているかもしれません。

  3. docker-composeを利用している理由としてはビルド時に環境変数をビルド環境に合わせて外から指定してあげたかったからです。dockerコマンドではコマンドごとに指定する必要があり、prod, stg, demoなどと複数環境ごとにイメージをビルドしたい場合に困ったので、docker-composeを用いています。

14
7
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
14
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?