28
28

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 3 years have passed since last update.

FastAPI + uvicorn + nginxをdocker-composeで構築

Last updated at Posted at 2020-08-15

目標

  • docker-composeを使い、NginxのリバースプロキシによってFastAPI(uvicornで起動)のアプリをhttpで公開する
    • まずは認証無しのhttp(80ポート)で公開

ファイル構成

shell(bashなど)
$ tree  
.
├── app
│   ├── Dockerfile
│   ├── app
│   │   ├── __init__.py
│   │   └── main.py
│   ├── poetry.lock
│   └── pyproject.toml
├── docker-compose.yml
└── web
    └── conf.d
        └── app.conf
  • FastAPIアプリはapp/Dockerfileを使ってイメージを作成
    • ソースコードはapp/app内に格納
    • Pythonのパッケージ管理はpyproject.tomlと必要であればpoetry.lockを使っている(Poetry使用)
  • Nginxの設定はweb/conf.d/app.confで行っている

docker-compose.yml

  • システム構成の概要について
docker-compose.yml
version: '3'

services:
  web:
    container_name: web
    image: nginx:alpine
    depends_on:
      # `app`サービス(コンテナ)が先に起動している必要があるので`depends_on`を指定しておく
      - app
    ports:
      # ポートマッピング: "ホストOSのポート:コンテナ(Nginx)のポート"
      - "80:80"
    volumes:
      # volumeマウント: "ホストOSのパス:コンテナにおけるパス"
      - ./web/conf.d:/etc/nginx/conf.d
    networks:
      - nginx_network

  app:
    container_name: app
    image: test_fastapi_app # ビルドされるDockerイメージ名を指定
    build:
      context: ./app
      dockerfile: Dockerfile
    expose:
      - 8000
    networks:
      - nginx_network
    # 例えばソースコードをリアルタイムに編集したいときは`volumes`でマウントすると便利
    # volumes:
    #   - ./app/app:/app/app
    # appコンテナの`CMD`を上書きするには`command`を使う
    # command: "uvicorn app.main:app --reload --host 0.0.0.0"

networks:
  nginx_network:
    driver: bridge
  • NginxのコンテナwebとFastAPIアプリのコンテナappを別個に作り、docker-composeのnetworks:nginx_networkで接続している
  • nginxのイメージは軽量なalpineバージョンを使っている

appおよびwebの中身の詳細は後述。

app (FastAPIアプリ本体)

Dockerfile

Dockerfile
ARG BASE_IMAGE=python:3.8-slim-buster                                                                                
FROM $BASE_IMAGE                                                                                                     
                                                                                                                     
# system update & package install                                                                                    
RUN apt-get -y update && \                                                                                           
    apt-get install -y --no-install-recommends \                                                                     
    build-essential \                                                                                                
    openssl libssl-dev \                                                                                             
    && apt-get clean \                                                                                               
    && rm -rf /var/lib/apt/lists/*                                                                                   
                                                                                                                     
# Add Tini                                                                                                           
ENV TINI_VERSION v0.19.0
ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini /tini
RUN chmod +x /tini
ENTRYPOINT ["/tini", "--"]

# 一般ユーザーアカウントを追加
ARG USER_NAME=app
ARG USER_UID=1000
ARG PASSWD=password

RUN useradd -m -s /bin/bash -u $USER_UID $USER_NAME && \
    gpasswd -a $USER_NAME sudo && \
    echo "${USER_NAME}:${PASSWD}" | chpasswd && \
    echo "${USER_NAME} ALL=(ALL) ALL" >> /etc/sudoers

# FastAPIのソースコードなどをコンテナ内へCOPY
COPY ./app /app/app
COPY ./pyproject.toml /app/pyproject.toml
# local環境で使用しているpoetry.lockファイルがあればそれも追加する
# COPY ./poetry.lock /app/poetry.lock
RUN chown -R ${USER_NAME}:${USER_NAME} /app


USER $USER_NAME
WORKDIR /app
# pip & poetry
ENV PATH $PATH:/home/${USER_NAME}/.local/bin
RUN python3 -m pip install --user --upgrade pip && \
    python3 -m pip install poetry --user && \
    poetry config virtualenvs.in-project true && \
    poetry install && \
    rm -rf ~/.cache/pip/* && \
    rm -rf ~/.cache/pypoetry/*

# Poetryで作った仮想環境にPATHを通しておく(予めpoetryのconfigを設定して仮想環境のPATHを一意にしておく)
ENV PATH=/app/.venv/bin:$PATH

# Configration
EXPOSE 8000

# Execute
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0"]
  • とりあえずPythonのバージョンは3.8を使用(公式イメージのslim-buster系(debianベース)のタグを利用)
  • debianベースなのでaptで必要なライブラリをインストール
  • 個人的にrootユーザーで起動したくないので、アプリを起動させる用のユーザーを作成
  • tiniは別に無くても動くが、containerを起動するときに使うとベターらしいのでGitHubでの説明にしたがって入れておく
  • コンテナの/appディレクトリ上にFastAPIアプリ用のファイル一式を格納し、WORKスペースにしている
    • ソースコードの本体は/app/app
  • ローカルユーザー権限でPoetryを使って必要なパッケージをインストールしている
    • poetry config virtualenvs.in-project trueによって/app/.venvに仮想環境が作られるようにしている
    • ローカル環境における開発などでpoetry.lockファイルがある場合、# COPY ./poetry.lock /app/poetry.lockのコメントアウトを外せばインストールするパッケージを固定出来る

pyproject.toml

  • インストールするパッケージの情報など
    • tool.poetry.dependenciesに必要最低限なパッケージ情報を記述していて、後は適当です
    • (必要に応じて公式ドキュメントなどを参照して追記・修正していく)
pyproject.toml
[tool.poetry]
name = "test_fastapi_app"
version = "0.1.0"
description = "just for test"
authors = ["Your Name <you@example.com>"]

[tool.poetry.dependencies]
python = "^3.8"
uvicorn = "*"
fastapi = "*"

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

FastAPIアプリ

  • 今回はごく最低限だけ準備

app/app/main.pyは以下のようにしてある:

main.py
"""
app main
"""

from fastapi import FastAPI

app = FastAPI()


@app.get('/')
async def site_root():
    """root"""
    return {"message": "Hello, WORLD!"}
  • (app/app/__init__.pyは空ファイル)

app/app/以下はそのときどきの目的・必要性に応じて今後作り込んでいく

uvicorn

FastAPIアプリの起動には、uvicorn公式ドキュメント)を使って、

uvicorn app.main:app --host 0.0.0.0

のようにする。(ディレクトリ名やファイル名が変わるとapp.main:appの部分も変わるはずなので注意する)
オプション:--host 0.0.0.0はコンテナ上で起動しているuvicornサーバーを外部から見れるようにするために必要。
uvicornサーバーはデフォルトだと8000ポートで起動するので、EXPOSE 8000などをDockerfiledocker-compose.yml上でやっておく。(もしかすると要らなかったかもしれない)

なお、--reloadオプションをつけるとファイルが変更されたときにuvicornサーバーが検知して再起動するようになるので、開発時などに便利

web (NginxによるWebサーバー)

  • appコンテナで立ち上げているuvicornサーバーのリバースプロキシ
  • /etc/nginx/conf.d/***.confに設定を書くことで各種設定が出来る
    • ***.confの名前は割となんでも良いらしい(今回はapp.confにしている)
app.conf
upstream backend {
    server app:8000;
}

server {
    listen 80;
    # server_name  localhost;

    location / {
        proxy_pass http://backend;
    }

    # log
    # access_log /var/log/nginx/access.log;
    # error_log /var/log/nginx/error.log;
}
  • upstreamのところでapp:8000のリバースプロキシの設定をしている
    • ここでのappdocker-compose.yml上で書いたservice名に対応していた気がする(違ってたらすみません)
    • location /のところでapp:8000の内容をNginxの/以下で公開している
      • 結果としてNginxの80ポートにアクセスするとFastAPI(uvicorn)の8000ポートが公開している内容が見れることになる
  • server_nameやログ出力が必要だったら適宜該当箇所をコメントアウトする

実行

  • よく使うもの:
# `app`のイメージをビルド
docker-compose build

# サービス全体の起動
docker-compose up -d

# 終了時
docker-compose down

# コンソール出力の確認
docker-compose logs
docker-compose logs app # appサービスのログのみ確認

# appコンテナの中に入る
docker-compose run app bash

うまく設定ができていれば、docker-compose up -dをして http://localhost および http://localhost/docs などにアクセスすると、

image.png

image.png

のような画面が確認出来る。
AWSのEC2など、リモート環境で実行しているときはlocalhostを実行しているマシンのアドレスに適宜置き換えればOKのはず。

まとめ

なるべくミニマムな内容でタイトルのような構成を作った。
今回はやっていないがNginxを使ってSSL化(HTTPS通信)やBASIC認証などをかけられる。
→ なお、Jinja2のTemplate機能を使っているときに、SSL化で結構ハマって手こずった(httpsがhttpになったり、デフォルト以外のポートを使うときにポート番号情報が欠落したりしてリンク機能がうまく働かなくなったりした)ので、別途まとめたる予定。(下記↓)

  1. FastAPI + uvicorn + NginxでWebページを表示(Jinja2によるTemplates機能 )
  2. FastAPI + uvicorn + NginxでWebページを表示(SSL/HTTPS化 )
28
28
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
28
28

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?