[PYTHON] Build FastAPI + uvicorn + nginx with docker-compose

Target

--Use docker-compose to publish FastAPI (launched with uvicorn) app on http by Nginx reverse proxy --First, publish on http (80 port) without authentication

file organization

shell (bash, etc.)


$ tree  
.
├── app
│   ├── Dockerfile
│   ├── app
│   │   ├── __init__.py
│   │   └── main.py
│   ├── poetry.lock
│   └── pyproject.toml
├── docker-compose.yml
└── web
    └── conf.d
        └── app.conf

--FastAPI apps create images using ʻapp / Dockerfile --Source code stored in ʻapp / app --Python package management uses pyproject.toml and poetry.lock if necessary (using Poetry) -(There is no deep reason, so you can easily do it with pip + requirements.txt Use Pipenv or miniconda I think there are various ways) --Nginx is set in web / conf.d / app.conf

docker-compose.yml

--Overview of system configuration

docker-compose.yml


version: '3'

services:
  web:
    container_name: web
    image: nginx:alpine
    depends_on:
      # `app`Because the service (container) needs to be started first`depends_on`Is specified
      - app
    ports:
      #Port mapping:"Host OS port:container(Nginx)Port"
      - "80:80"
    volumes:
      #volume mount:"Host OS path:Path in the container"
      - ./web/conf.d:/etc/nginx/conf.d
    networks:
      - nginx_network

  app:
    container_name: app
    image: test_fastapi_app #Specify the name of the Docker image to be built
    build:
      context: ./app
      dockerfile: Dockerfile
    expose:
      - 8000
    networks:
      - nginx_network
    #For example, if you want to edit the source code in real time`volumes`Convenient to mount with
    # volumes:
    #   - ./app/app:/app/app
    #of the app container`CMD`To overwrite`command`use
    # command: "uvicorn app.main:app --reload --host 0.0.0.0"

networks:
  nginx_network:
    driver: bridge

--Nginx container web and FastAPI app container ʻapp are created separately and connected by docker-compose networks: nginx_network`. --The nginx image uses a lightweight alpine version

Details of the contents of ʻapp and web` will be described later.

ʻApp` (Fast API app body)

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", "--"]

#Add general user account
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

#COPY FastAPI source code etc. into the container
COPY ./app /app/app
COPY ./pyproject.toml /app/pyproject.toml
#Poetry used in local environment.Add the lock file, if any
# 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/*

#Put the PATH in the virtual environment created by Poetry (set the poetry config in advance to make the PATH of the virtual environment unique)
ENV PATH=/app/.venv/bin:$PATH

# Configration
EXPOSE 8000

# Execute
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0"]

--For the time being, use Python version 3.8 (using the official image slim-buster series (debian-based) tags) --Since it is based on debian, install the required libraries with ʻapt --I personally don't want to start it as a root user, so I created a user to start the app -[tini](https://github.com/krallin/tini) works without it, but it seems better to use it when starting the container, so [Explanation on GitHub](https://github) .com / krallin / tini # using-tini) --A set of files for the FastAPI app is stored in the / appdirectory of the container and used as a WORK space. --The body of the source code is/ app / app --Installing the required packages usingPoetry with local user privileges --poetry config virtualenvs.in-project true allows a virtual environment to be created in /app/.venv --If you have apoetry.lock file for development in a local environment, you can fix the package to be installed by uncommenting # COPY ./poetry.lock /app/poetry.lock`.

pyproject.toml

--Information on packages to install, etc. --The minimum required package information is described in tool.poetry.dependencies, and the rest is appropriate. -(Additions and corrections will be made by referring to Official Documents etc. as necessary)

pyproject.toml


[tool.poetry]
name = "test_fastapi_app"
version = "0.1.0"
description = "just for test"
authors = ["Your Name <[email protected]>"]

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

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

FastAPI app

――This time, prepare only the minimum

ʻApp / app / main.py` looks like this:

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` is an empty file)

ʻApp / app /` The following will be created in the future according to the purpose and necessity of each occasion.

uvicorn

To launch the FastAPI app, use ʻuvicorn` (Official documentation)

uvicorn app.main:app --host 0.0.0.0

To do. (Note that if the directory name or file name changes, the part of ʻapp.main: appshould also change.) Optional:--host 0.0.0.0 is required to allow the uvicorn server running on the container to be seen externally. The uvicorn server starts on port 8000 by default, so do something like ʻEXPOSE 8000 on Dockerfile or docker-compose.yml. (Maybe I didn't need it)

If you add the --reload option, the uvicorn server will detect when the file is changed and restart it, which is convenient for development etc.

web (Web server by Nginx)

--ʻAppReverse proxy of uvicorn server running in container --Various settings can be made by writing the settings in/etc/nginx/conf.d/.conf. --It seems that the name of . conf can be anything (this time it is ʻ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;
}

--At ʻupstream, the reverse proxy of ʻapp: 8000 is set. --I feel that ʻapp here corresponds to the service name written on docker-compose.yml(I'm sorry if it's different). --Atlocation /, the contents of ʻapp: 8000 are published under/of Nginx. --As a result, when you access port 80 of Nginx, you can see the contents published by port 8000 of FastAPI (uvicorn). --If you need server_name or log output, comment out the relevant part as appropriate.

Run

--Frequently used items:

# `app`Build image of
docker-compose build

#Starting the entire service
docker-compose up -d

#When finished
docker-compose down

#Check console output
docker-compose logs
docker-compose logs app #Check only app service log

#Go inside the app container
docker-compose run app bash

If the settings are correct, you can do docker-compose up -d to access http: // localhost and http: // localhost / docs, etc.

--http: // localhost (when running in local environment)

image.png

--http: // localhost / docs (when running in local environment)

image.png

You can see the screen like. When running in a remote environment such as EC2 on AWS, it should be OK if you replace localhost with the address of the machine running.

Summary

I made a title-like structure with as minimal content as possible. I haven't done this this time, but you can use Nginx for SSL (HTTPS communication) and BASIC authentication. → In addition, when using the Template function of Jinja2, I was quite addicted to SSL conversion (https became http, port number information was missing when using a port other than the default, and the link function was not available. It didn't work well), so I'll summarize it separately ~~. (↓ below)

  1. Display Web page with FastAPI + uvicorn + Nginx (Templates function by Jinja2)
  2. Display Web page with FastAPI + uvicorn + Nginx (SSL / HTTPS)

Recommended Posts

Build FastAPI + uvicorn + nginx with docker-compose
Build a Django development environment with Docker! (Docker-compose / Django / postgreSQL / nginx)
Display a web page with FastAPI + uvicorn + Nginx (SSL / HTTPS)
Build a Fast API environment with docker-compose
Build NGINX + NGINX Unit + MySQL environment with Docker
Display a web page with FastAPI + uvicorn + Nginx (Templates function by Jinja2)
Build Django + NGINX + PostgreSQL development environment with Docker
Build the fastest Django development environment with docker-compose
Python environment with docker-compose
Build python3.x with pyenv
Create a django environment with docker-compose (MariaDB + Nginx + uWSGI)
[blackbird-nginx] Monitor Nginx with blackbird
API with Flask + uWSGI + Nginx
Control error wording with nginx
Build python3 environment with ubuntu 16.04
Combine FastAPI with Django ORM
Build python environment with direnv
Easily build CNN with Keras
Let's build git-cat with Python
Django development environment construction with Docker-compose + Nginx + uWSGI + MariaDB (macOS edition)
Create a development environment for Go + MySQL + nginx with Docker (docker-compose)
[DynamoDB] [Docker] Build a development environment for DynamoDB and Django with docker-compose