Magicode logo
Magicode
1
10 min read

Docker&FastAPI開発環境について考えてみた

https://cdn.apollon.ai/media/notebox/blob_9nk9uSk

はじめに

前回の続き
Dockerイメージ動かせるところまでやったけど、開発環境として準備することあるのでは?という検討のメモです。
なお、執筆は2022年5月だが作業は1月末に行っている。

環境

  • Windows 10
  • Docker Desktop
  • Visual Studio Code

自分の中の要求整理

最適解というより、勉強としてこうしてみたいという要望
  1. なるべくさぼりたい
  2. development, productionなど環境を分けられるようにしたい
  3. poetoryかpipenvでmodule管理したい
  4. まっさらな状態からスタートしたい(すでにコードがあって、それをdockerにするではない)
  5. apiのロジックを書いたときにコンテナの再起動を不要にしたい(開発環境では)

Dockerfileとdocker-composeの準備

この記事が似た考えだったので、これをベースに進める。
最終的な成果物は末尾に記載。

マルチステージビルド

dockerのビルドを複数の ステージ に分けて実行できる機能で、別ステージのビルド結果を参照できるようになり、容量が抑えられる(らしい)。
参考
この機能を使って、initail, development, productionに分け initialでは初期の環境構築、developmentでは開発時の、productionでリリース時のビルドのみに集中する。

initial

apt-getで必要なツールのインストールとpoetory のインストールをしている。
$ docker-compose -f docker-compose.initial.yml build でビルドできる。

develop

poetoryによるパッケージのインストールを実施する。
COPY poetry.lock pyproject.toml ./ とあり、
どうも、poetory関連のファイルをcopyしている(正確にはdevelopmentではなくdevelopment-baseで) 。
だが、まっさらな状態ではそれらのファイルは存在しないはずでちゃんと動かなそう。
実際に、$ docker-compose -f docker-compose.development.yml build でビルドを実行すると下記のエラー。
Building app
[+] Building 1.6s (9/11)
 => [internal] load build definition from Dockerfile                                                                                                                                                                                       0.0s 
 => => transferring dockerfile: 32B                                                                                                                                                                                                        0.0s 
 => [internal] load .dockerignore                                                                                                                                                                                                          0.0s 
 => => transferring context: 2B                                                                                                                                                                                                            0.0s 
 => [internal] load metadata for docker.io/tiangolo/uvicorn-gunicorn-fastapi:python3.9                                                                                                                                                     1.5s 
 => [python-base 1/1] FROM docker.io/tiangolo/uvicorn-gunicorn-fastapi:python3.9@sha256:d0ffdbeffb4e713782dd776b1b46311cb94952f4d00098330c393b0b81ca2881                                                                                   0.0s 
 => [internal] load build context                                                                                                                                                                                                          0.0s 
 => => transferring context: 2B                                                                                                                                                                                                            0.0s 
 => CACHED [initial 1/3] RUN apt-get update     && apt-get install --no-install-recommends -y         curl         build-essential         git         cmake                                                                               0.0s 
 => CACHED [initial 2/3] RUN curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py | python                                                                                                                0.0s 
 => CACHED [initial 3/3] WORKDIR /opt/pysetup                                                                                                                                                                                              0.0s 
 => ERROR [development-base 1/1] COPY poetry.lock pyproject.toml ./                                                                                                                                                                        0.0s 
------
 > [development-base 1/1] COPY poetry.lock pyproject.toml ./:
------
failed to compute cache key: "/pyproject.toml" not found: not found
ERROR: Service 'app' failed to build : Build failed
ファイルがないので当然のエラー。

調査

initialのタイミングで poetory init でpyproject.tomlを生成し、それがホストにも同期されればよいのでは?と考え
volumeでマウントした状態で RUN poetry init --no-interactionしてみた※が、pyproject.tomlは生成されなかった。
これは、ここにあるように、マウントしていてもDockerfile内で実行されたものは後からホストマシンのディレクトリが上書きしてしまうからと考えられる。
※ 手順
  1. Dockerileで、FROM initial as development-baseの前に、 poetory init --no-interaction を挿入する
  2. docker-compose.initial.ymlで、volumeの設定をする。(下記をservices.app以下に配置)
volumes:
  - ./:/app

暫定対応

仕方がないので、コンテナに入り、手動で poetory init した。
コンテナには、
$ docker-compose -f docker-compose.initial.yml run app bash
で入ることができる。

production

ちゃんと確認していないので飛ばす。

開発時にいちいちコンテナを再起動しなくて済むように

Docker イメージ制作者が `start-reload.sh' というのを用意してくれている。
docker-compose.development.ymlで service.app配下に下記のcommandを挿入することで、実現できる
command: /bin/bash /start-reload.sh

その他

開発時に80番ポートは嫌だったので8080ポートに変更した。
docker-compose.development.yml の portsを8080:80にすればよい。

終わりに

fastapiをとりあえず動かせる状態から、開発進めはじめるための環境構築をした。
やり始めるといろいろあると思うが、ひとまずは準備できたと思う。

最終成果物

Dockerfile
FROM tiangolo/uvicorn-gunicorn-fastapi:python3.9 as python-base

ENV PYTHONUNBUFFERED=1 \
    PYTHONDONTWRITEBYTECODE=1 \
    \
    PIP_NO_CACHE_DIR=off \
    PIP_DISABLE_PIP_VERSION_CHECK=on \
    PIP_DEFAULT_TIMEOUT=100 \
    \
    POETRY_HOME="/opt/poetry" \
    \POETRY_VIRTUALENVS_CREATE=false \
    \
    PYSETUP_PATH="/opt/pysetup"

ENV PATH="$POETRY_HOME/bin:$PATH"

FROM python-base as initial
RUN apt-get update \
    && apt-get install --no-install-recommends -y \
        curl \
        build-essential \
        git \
        cmake

RUN curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py | python

WORKDIR $PYSETUP_PATH

FROM initial as development-base
ENV POETRY_NO_INTERACTION=1
COPY poetry.lock pyproject.toml ./

FROM development-base as development
RUN poetry install

WORKDIR /app

FROM development-base as builder-base
RUN poetry install --no-dev

FROM python-base as production
COPY --from=builder-base /usr/local/lib/python3.9/site-packages /usr/local/lib/python3.9/site-packages
COPY ./app /app/
WORKDIR /app
docker-compose.initial.yml
version: "3.8"
services:
  app:
    build:
      context: ./
      dockerfile: Dockerfile
      target: initial
    volumes:
      - ./:/opt/pysetup
      #- ./:/app
    tty: true
docker-compose.development.yml
version: "3.8"
services:
  app:
    build:
      context: ./
      dockerfile: Dockerfile
      target: development
    volumes:
      - ./:/app
    tty: true
    ports:
      - "8080:80"
    command: /bin/bash /start-reload.sh
docker-compose.production.yml
version: "3.8"
services:
  app:
    build:
      context: ./
      dockerfile: Dockerfile
      target: production

Discussion

コメントにはログインが必要です。