--Use docker-compose to publish FastAPI (launched with uvicorn) app on http by Nginx reverse proxy --First, publish on http (80 port) without authentication
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.
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"
――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.
--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)

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

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.
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)
Recommended Posts