--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 using
Poetry with local user privileges --
poetry config virtualenvs.in-project true allows a virtual environment to be created in
/app/.venv --If you have a
poetry.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). --At
location /, 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